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.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.openhab.binding.enocean.internal.eep.Base.UTEResponse;
21 import org.openhab.binding.enocean.internal.eep.Base._4BSMessage;
22 import org.openhab.binding.enocean.internal.eep.Base._4BSTeachInVariation3Response;
23 import org.openhab.binding.enocean.internal.eep.Base._RPSMessage;
24 import org.openhab.binding.enocean.internal.eep.D5_00.D5_00_01;
25 import org.openhab.binding.enocean.internal.eep.F6_01.F6_01_01;
26 import org.openhab.binding.enocean.internal.eep.F6_02.F6_02_01;
27 import org.openhab.binding.enocean.internal.eep.F6_05.F6_05_02;
28 import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00;
29 import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00_EltakoFPE;
30 import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_01;
31 import org.openhab.binding.enocean.internal.messages.ERP1Message;
32 import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
33 import org.openhab.binding.enocean.internal.messages.EventMessage;
34 import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType;
35 import org.openhab.binding.enocean.internal.messages.Responses.SMACKTeachInResponse;
36 import org.openhab.core.util.HexUtils;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
42 * @author Daniel Weber - Initial contribution
44 public class EEPFactory {
46 private static final Logger logger = LoggerFactory.getLogger(EEPFactory.class);
48 public static EEP createEEP(EEPType eepType) {
50 Class<? extends EEP> cl = eepType.getEEPClass();
52 throw new IllegalArgumentException("Message " + eepType + " not implemented");
54 return cl.getDeclaredConstructor().newInstance();
55 } catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException
56 | NoSuchMethodException | SecurityException e) {
57 throw new IllegalArgumentException(e);
61 public static EEP buildEEP(EEPType eepType, ERP1Message packet) {
63 Class<? extends EEP> cl = eepType.getEEPClass();
65 throw new IllegalArgumentException("Message " + eepType + " not implemented");
67 return cl.getConstructor(ERP1Message.class).newInstance(packet);
68 } catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException
69 | NoSuchMethodException | SecurityException e) {
70 logger.error("Cannot instantiate EEP {}-{}-{}: {}",
71 HexUtils.bytesToHex(new byte[] { eepType.getRORG().getValue() }),
72 HexUtils.bytesToHex(new byte[] { (byte) eepType.getFunc() }),
73 HexUtils.bytesToHex(new byte[] { (byte) eepType.getType() }), e.getMessage());
75 throw new IllegalArgumentException(e);
79 private static EEPType getGenericEEPTypeFor(byte rorg) {
80 logger.info("Received unsupported EEP teach in, trying to fallback to generic thing");
81 RORG r = RORG.getRORG(rorg);
83 logger.info("Fallback to 4BS generic thing");
84 return EEPType.Generic4BS;
85 } else if (r == RORG.VLD) {
86 logger.info("Fallback to VLD generic thing");
87 return EEPType.GenericVLD;
89 logger.info("Fallback not possible");
94 public static EEP buildEEPFromTeachInERP1(ERP1Message msg) {
95 if (!msg.getIsTeachIn() && !(msg.getRORG() == RORG.RPS)) {
99 switch (msg.getRORG()) {
102 _RPSMessage result = new F6_10_00(msg);
103 if (result.isValidForTeachIn()) {
106 } catch (Exception e) {
110 _RPSMessage result = new F6_10_01(msg);
111 if (result.isValidForTeachIn()) {
114 } catch (Exception e) {
118 _RPSMessage result = new F6_02_01(msg);
119 if (result.isValidForTeachIn()) {
122 } catch (Exception e) {
126 _RPSMessage result = new F6_05_02(msg);
127 if (result.isValidForTeachIn()) {
130 } catch (Exception e) {
134 _RPSMessage result = new F6_01_01(msg);
135 if (result.isValidForTeachIn()) {
138 } catch (Exception e) {
142 _RPSMessage result = new F6_10_00_EltakoFPE(msg);
143 if (result.isValidForTeachIn()) {
146 } catch (Exception e) {
151 return new D5_00_01(msg);
153 int db_0 = msg.getPayload()[4];
154 if ((db_0 & _4BSMessage.LRN_Type_Mask) == 0) { // Variation 1
155 logger.info("Received 4BS Teach In variation 1 without EEP, fallback to generic thing");
156 return buildEEP(EEPType.Generic4BS, msg);
159 byte db_3 = msg.getPayload()[1];
160 byte db_2 = msg.getPayload()[2];
161 byte db_1 = msg.getPayload()[3];
163 int func = (db_3 & 0xFF) >>> 2;
164 int type = ((db_3 & 0b11) << 5) + ((db_2 & 0xFF) >>> 3);
165 int manufId = ((db_2 & 0b111) << 8) + (db_1 & 0xff);
167 logger.debug("Received 4BS Teach In with EEP A5-{}-{} and manufacturerID {}",
168 HexUtils.bytesToHex(new byte[] { (byte) func }),
169 HexUtils.bytesToHex(new byte[] { (byte) type }),
170 HexUtils.bytesToHex(new byte[] { (byte) manufId }));
172 EEPType eepType = EEPType.getType(RORG._4BS, func, type, manufId);
173 if (eepType == null) {
174 eepType = getGenericEEPTypeFor(RORG._4BS.getValue());
177 if (eepType != null) {
178 return buildEEP(eepType, msg);
183 byte[] payload = msg.getPayload();
185 byte rorg = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH];
186 byte func = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH];
187 byte type = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH
190 byte manufIdMSB = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH
191 - ESP3_RORG_LENGTH - 2];
192 byte manufIdLSB = payload[payload.length - 1 - ESP3_STATUS_LENGTH - ESP3_SENDERID_LENGTH
193 - ESP3_RORG_LENGTH - 3];
194 int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff);
196 EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
197 if (eepType == null) {
198 eepType = getGenericEEPTypeFor(rorg);
201 if (eepType != null) {
202 return buildEEP(eepType, msg);
213 public static EEP buildEEPFromTeachInSMACKEvent(EventMessage event) {
214 if (event.getEventMessageType() != EventMessageType.SA_CONFIRM_LEARN) {
218 byte[] payload = event.getPayload();
219 byte manufIdMSB = payload[2];
220 byte manufIdLSB = payload[3];
221 int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff);
223 byte rorg = payload[4];
224 int func = payload[5] & 0xFF;
225 int type = payload[6] & 0xFF;
227 byte[] senderId = Arrays.copyOfRange(payload, 12, 12 + 4);
229 logger.debug("Received SMACK Teach In with EEP {}-{}-{} and manufacturerID {}",
230 HexUtils.bytesToHex(new byte[] { (byte) rorg }), HexUtils.bytesToHex(new byte[] { (byte) func }),
231 HexUtils.bytesToHex(new byte[] { (byte) type }), HexUtils.bytesToHex(new byte[] { (byte) manufId }));
233 EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
234 if (eepType == null) {
235 eepType = getGenericEEPTypeFor(rorg);
238 return createEEP(eepType).setSenderId(senderId);
241 public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId, boolean teachIn) {
242 switch (msg.getRORG()) {
244 EEP result = new UTEResponse(msg, teachIn);
245 result.setSenderId(senderId);
249 result = new _4BSTeachInVariation3Response(msg, teachIn);
250 result.setSenderId(senderId);
258 public static SMACKTeachInResponse buildResponseFromSMACKTeachIn(EventMessage event, boolean sendTeachOuts) {
259 SMACKTeachInResponse response = new SMACKTeachInResponse();
261 byte priority = event.getPayload()[1];
262 if ((priority & 0b1001) == 0b1001) {
263 logger.debug("gtw is already postmaster");
265 logger.debug("Repeated learn is not allow hence send teach out");
266 response.setTeachOutResponse();
268 logger.debug("Send a repeated learn in");
269 response.setRepeatedTeachInResponse();
271 } else if ((priority & 0b100) == 0) {
272 logger.debug("no place for further mailbox");
273 response.setNoPlaceForFurtherMailbox();
274 } else if ((priority & 0b10) == 0) {
275 logger.debug("rssi is not good enough");
276 response.setBadRSSI();
277 } else if ((priority & 0b1) == 0b1) {
278 logger.debug("gtw is candidate for postmaster => teach in");
279 response.setTeachIn();