]> git.basschouten.com Git - openhab-addons.git/blob
276f609d584317ccba8a25e35eab998c9b4f22a8
[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.enocean.internal.eep;
14
15 import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*;
16
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.Arrays;
19
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;
39
40 /**
41  *
42  * @author Daniel Weber - Initial contribution
43  */
44 public class EEPFactory {
45
46     private static final Logger logger = LoggerFactory.getLogger(EEPFactory.class);
47
48     public static EEP createEEP(EEPType eepType) {
49         try {
50             Class<? extends EEP> cl = eepType.getEEPClass();
51             if (cl == null) {
52                 throw new IllegalArgumentException("Message " + eepType + " not implemented");
53             }
54             return cl.getDeclaredConstructor().newInstance();
55         } catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException
56                 | NoSuchMethodException | SecurityException e) {
57             throw new IllegalArgumentException(e);
58         }
59     }
60
61     public static EEP buildEEP(EEPType eepType, ERP1Message packet) {
62         try {
63             Class<? extends EEP> cl = eepType.getEEPClass();
64             if (cl == null) {
65                 throw new IllegalArgumentException("Message " + eepType + " not implemented");
66             }
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());
74
75             throw new IllegalArgumentException(e);
76         }
77     }
78
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);
82         if (r == RORG._4BS) {
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;
88         } else {
89             logger.info("Fallback not possible");
90             return null;
91         }
92     }
93
94     public static EEP buildEEPFromTeachInERP1(ERP1Message msg) {
95         if (!msg.getIsTeachIn() && !(msg.getRORG() == RORG.RPS)) {
96             return null;
97         }
98
99         switch (msg.getRORG()) {
100             case RPS:
101                 try {
102                     _RPSMessage result = new F6_10_00(msg);
103                     if (result.isValidForTeachIn()) {
104                         return result;
105                     }
106                 } catch (Exception e) {
107                 }
108
109                 try {
110                     _RPSMessage result = new F6_10_01(msg);
111                     if (result.isValidForTeachIn()) {
112                         return result;
113                     }
114                 } catch (Exception e) {
115                 }
116
117                 try {
118                     _RPSMessage result = new F6_02_01(msg);
119                     if (result.isValidForTeachIn()) {
120                         return result;
121                     }
122                 } catch (Exception e) {
123                 }
124
125                 try {
126                     _RPSMessage result = new F6_05_02(msg);
127                     if (result.isValidForTeachIn()) {
128                         return result;
129                     }
130                 } catch (Exception e) {
131                 }
132
133                 try {
134                     _RPSMessage result = new F6_01_01(msg);
135                     if (result.isValidForTeachIn()) {
136                         return result;
137                     }
138                 } catch (Exception e) {
139                 }
140
141                 try {
142                     _RPSMessage result = new F6_10_00_EltakoFPE(msg);
143                     if (result.isValidForTeachIn()) {
144                         return result;
145                     }
146                 } catch (Exception e) {
147                 }
148
149                 return null;
150             case _1BS:
151                 return new D5_00_01(msg);
152             case _4BS: {
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);
157                 }
158
159                 byte db_3 = msg.getPayload()[1];
160                 byte db_2 = msg.getPayload()[2];
161                 byte db_1 = msg.getPayload()[3];
162
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);
166
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 }));
171
172                 EEPType eepType = EEPType.getType(RORG._4BS, func, type, manufId);
173                 if (eepType == null) {
174                     eepType = getGenericEEPTypeFor(RORG._4BS.getValue());
175                 }
176
177                 if (eepType != null) {
178                     return buildEEP(eepType, msg);
179                 }
180             }
181                 break;
182             case UTE: {
183                 byte[] payload = msg.getPayload();
184
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
188                         - 1];
189
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);
195
196                 EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
197                 if (eepType == null) {
198                     eepType = getGenericEEPTypeFor(rorg);
199                 }
200
201                 if (eepType != null) {
202                     return buildEEP(eepType, msg);
203                 }
204             }
205                 break;
206             default:
207                 return null;
208         }
209
210         return null;
211     }
212
213     public static EEP buildEEPFromTeachInSMACKEvent(EventMessage event) {
214         if (event.getEventMessageType() != EventMessageType.SA_CONFIRM_LEARN) {
215             return null;
216         }
217
218         byte[] payload = event.getPayload();
219         byte manufIdMSB = payload[2];
220         byte manufIdLSB = payload[3];
221         int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff);
222
223         byte rorg = payload[4];
224         int func = payload[5] & 0xFF;
225         int type = payload[6] & 0xFF;
226
227         byte[] senderId = Arrays.copyOfRange(payload, 12, 12 + 4);
228
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 }));
232
233         EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
234         if (eepType == null) {
235             eepType = getGenericEEPTypeFor(rorg);
236         }
237
238         return createEEP(eepType).setSenderId(senderId);
239     }
240
241     public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId, boolean teachIn) {
242         switch (msg.getRORG()) {
243             case UTE:
244                 EEP result = new UTEResponse(msg, teachIn);
245                 result.setSenderId(senderId);
246
247                 return result;
248             case _4BS:
249                 result = new _4BSTeachInVariation3Response(msg, teachIn);
250                 result.setSenderId(senderId);
251
252                 return result;
253             default:
254                 return null;
255         }
256     }
257
258     public static SMACKTeachInResponse buildResponseFromSMACKTeachIn(EventMessage event, boolean sendTeachOuts) {
259         SMACKTeachInResponse response = new SMACKTeachInResponse();
260
261         byte priority = event.getPayload()[1];
262         if ((priority & 0b1001) == 0b1001) {
263             logger.debug("gtw is already postmaster");
264             if (sendTeachOuts) {
265                 logger.debug("Repeated learn is not allow hence send teach out");
266                 response.setTeachOutResponse();
267             } else {
268                 logger.debug("Send a repeated learn in");
269                 response.setRepeatedTeachInResponse();
270             }
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();
280         }
281
282         return response;
283     }
284 }