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