2 * Copyright (c) 2010-2024 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.enocean.internal.eep;
15 import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*;
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.Arrays;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.enocean.internal.eep.Base.UTEResponse;
23 import org.openhab.binding.enocean.internal.eep.Base._4BSMessage;
24 import org.openhab.binding.enocean.internal.eep.Base._4BSTeachInVariation3Response;
25 import org.openhab.binding.enocean.internal.eep.Base._RPSMessage;
26 import org.openhab.binding.enocean.internal.eep.D5_00.D5_00_01;
27 import org.openhab.binding.enocean.internal.eep.F6_01.F6_01_01;
28 import org.openhab.binding.enocean.internal.eep.F6_02.F6_02_01;
29 import org.openhab.binding.enocean.internal.eep.F6_05.F6_05_02;
30 import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00;
31 import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00_EltakoFPE;
32 import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_01;
33 import org.openhab.binding.enocean.internal.messages.ERP1Message;
34 import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
35 import org.openhab.binding.enocean.internal.messages.EventMessage;
36 import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType;
37 import org.openhab.binding.enocean.internal.messages.responses.SMACKTeachInResponse;
38 import org.openhab.core.util.HexUtils;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
44 * @author Daniel Weber - Initial contribution
47 public class EEPFactory {
49 private static final Logger logger = LoggerFactory.getLogger(EEPFactory.class);
51 public static EEP createEEP(EEPType eepType) {
53 Class<? extends EEP> cl = eepType.getEEPClass();
55 throw new IllegalArgumentException("Message " + eepType + " not implemented");
57 return cl.getDeclaredConstructor().newInstance();
58 } catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException
59 | NoSuchMethodException | SecurityException e) {
60 throw new IllegalArgumentException(e);
64 public static EEP buildEEP(EEPType eepType, ERP1Message packet) {
66 Class<? extends EEP> cl = eepType.getEEPClass();
68 throw new IllegalArgumentException("Message " + eepType + " not implemented");
70 return cl.getConstructor(ERP1Message.class).newInstance(packet);
71 } catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException
72 | NoSuchMethodException | SecurityException e) {
73 logger.error("Cannot instantiate EEP {}-{}-{}: {}",
74 HexUtils.bytesToHex(new byte[] { eepType.getRORG().getValue() }),
75 HexUtils.bytesToHex(new byte[] { (byte) eepType.getFunc() }),
76 HexUtils.bytesToHex(new byte[] { (byte) eepType.getType() }), e.getMessage());
78 throw new IllegalArgumentException(e);
82 private static @Nullable EEPType getGenericEEPTypeFor(byte rorg) {
83 logger.info("Received unsupported EEP teach in, trying to fallback to generic thing");
84 RORG r = RORG.getRORG(rorg);
86 logger.info("Fallback to 4BS generic thing");
87 return EEPType.Generic4BS;
88 } else if (r == RORG.VLD) {
89 logger.info("Fallback to VLD generic thing");
90 return EEPType.GenericVLD;
92 logger.info("Fallback not possible");
97 public static @Nullable EEP buildEEPFromTeachInERP1(ERP1Message msg) {
98 if (!msg.getIsTeachIn() && msg.getRORG() != RORG.RPS) {
102 switch (msg.getRORG()) {
105 _RPSMessage result = new F6_10_00(msg);
106 if (result.isValidForTeachIn()) {
109 } catch (Exception e) {
113 _RPSMessage result = new F6_10_01(msg);
114 if (result.isValidForTeachIn()) {
117 } catch (Exception e) {
121 _RPSMessage result = new F6_02_01(msg);
122 if (result.isValidForTeachIn()) {
125 } catch (Exception e) {
129 _RPSMessage result = new F6_05_02(msg);
130 if (result.isValidForTeachIn()) {
133 } catch (Exception e) {
137 _RPSMessage result = new F6_01_01(msg);
138 if (result.isValidForTeachIn()) {
141 } catch (Exception e) {
145 _RPSMessage result = new F6_10_00_EltakoFPE(msg);
146 if (result.isValidForTeachIn()) {
149 } catch (Exception e) {
154 return new D5_00_01(msg);
156 int db0 = msg.getPayload()[4];
157 if ((db0 & _4BSMessage.LRN_TYPE_MASK) == 0) { // Variation 1
158 logger.info("Received 4BS Teach In variation 1 without EEP, fallback to generic thing");
159 return buildEEP(EEPType.Generic4BS, msg);
162 byte db3 = msg.getPayload()[1];
163 byte db2 = msg.getPayload()[2];
164 byte db1 = msg.getPayload()[3];
166 int func = (db3 & 0xFF) >>> 2;
167 int type = ((db3 & 0b11) << 5) + ((db2 & 0xFF) >>> 3);
168 int manufId = ((db2 & 0b111) << 8) + (db1 & 0xff);
170 logger.debug("Received 4BS Teach In with EEP A5-{}-{} and manufacturerID {}",
171 HexUtils.bytesToHex(new byte[] { (byte) func }),
172 HexUtils.bytesToHex(new byte[] { (byte) type }),
173 HexUtils.bytesToHex(new byte[] { (byte) manufId }));
175 EEPType eepType = EEPType.getType(RORG._4BS, func, type, manufId);
176 if (eepType == null) {
177 eepType = getGenericEEPTypeFor(RORG._4BS.getValue());
180 if (eepType != null) {
181 return buildEEP(eepType, msg);
186 byte[] payload = msg.getPayload();
188 byte rorg = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH];
189 byte func = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH];
190 byte type = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH
193 byte manufIdMSB = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH
194 - ESP3_RORG_LENGTH - 2];
195 byte manufIdLSB = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH
196 - ESP3_RORG_LENGTH - 3];
197 int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff);
199 EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
200 if (eepType == null) {
201 eepType = getGenericEEPTypeFor(rorg);
204 if (eepType != null) {
205 return buildEEP(eepType, msg);
216 public static @Nullable EEP buildEEPFromTeachInSMACKEvent(EventMessage event) {
217 if (event.getEventMessageType() != EventMessageType.SA_CONFIRM_LEARN) {
221 byte[] payload = event.getPayload();
222 byte manufIdMSB = payload[2];
223 byte manufIdLSB = payload[3];
224 int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff);
226 byte rorg = payload[4];
227 int func = payload[5] & 0xFF;
228 int type = payload[6] & 0xFF;
230 byte[] senderId = Arrays.copyOfRange(payload, 12, 12 + 4);
232 logger.debug("Received SMACK Teach In with EEP {}-{}-{} and manufacturerID {}",
233 HexUtils.bytesToHex(new byte[] { (byte) rorg }), HexUtils.bytesToHex(new byte[] { (byte) func }),
234 HexUtils.bytesToHex(new byte[] { (byte) type }), HexUtils.bytesToHex(new byte[] { (byte) manufId }));
236 EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
237 if (eepType == null) {
238 eepType = getGenericEEPTypeFor(rorg);
241 return (eepType == null) ? null : createEEP(eepType).setSenderId(senderId);
244 public static @Nullable EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId, boolean teachIn) {
245 switch (msg.getRORG()) {
247 EEP result = new UTEResponse(msg, teachIn);
248 result.setSenderId(senderId);
252 result = new _4BSTeachInVariation3Response(msg, teachIn);
253 result.setSenderId(senderId);
261 public static SMACKTeachInResponse buildResponseFromSMACKTeachIn(EventMessage event, boolean sendTeachOuts) {
262 SMACKTeachInResponse response = new SMACKTeachInResponse();
264 byte priority = event.getPayload()[1];
265 if ((priority & 0b1001) == 0b1001) {
266 logger.debug("gtw is already postmaster");
268 logger.debug("Repeated learn is not allow hence send teach out");
269 response.setTeachOutResponse();
271 logger.debug("Send a repeated learn in");
272 response.setRepeatedTeachInResponse();
274 } else if ((priority & 0b100) == 0) {
275 logger.debug("no place for further mailbox");
276 response.setNoPlaceForFurtherMailbox();
277 } else if ((priority & 0b10) == 0) {
278 logger.debug("rssi is not good enough");
279 response.setBadRSSI();
280 } else if ((priority & 0b1) == 0b1) {
281 logger.debug("gtw is candidate for postmaster => teach in");
282 response.setTeachIn();