2 * Copyright (c) 2010-2020 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.discovery;
15 import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
19 import org.eclipse.jdt.annotation.NonNull;
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.EEP;
23 import org.openhab.binding.enocean.internal.eep.EEPFactory;
24 import org.openhab.binding.enocean.internal.handler.EnOceanBridgeHandler;
25 import org.openhab.binding.enocean.internal.messages.BasePacket;
26 import org.openhab.binding.enocean.internal.messages.ERP1Message;
27 import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
28 import org.openhab.binding.enocean.internal.transceiver.PacketListener;
29 import org.openhab.core.config.discovery.AbstractDiscoveryService;
30 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
31 import org.openhab.core.thing.ThingTypeUID;
32 import org.openhab.core.thing.ThingUID;
33 import org.openhab.core.util.HexUtils;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * The {@link EnOceanDeviceDiscoveryService} is used to discover Enocean devices and to accept teach in requests.
40 * @author Daniel Weber - Initial contribution
43 public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements PacketListener {
44 private final Logger logger = LoggerFactory.getLogger(EnOceanDeviceDiscoveryService.class);
46 private EnOceanBridgeHandler bridgeHandler;
48 public EnOceanDeviceDiscoveryService(EnOceanBridgeHandler bridgeHandler) {
49 super(null, 60, false);
50 this.bridgeHandler = bridgeHandler;
54 * Called on component activation.
56 public void activate() {
61 public void deactivate() {
66 protected void startScan() {
67 if (bridgeHandler == null) {
71 logger.info("Starting EnOcean discovery and accepting teach in requests");
72 bridgeHandler.startDiscovery(this);
76 public synchronized void stopScan() {
77 if (bridgeHandler == null) {
81 logger.info("Stopping EnOcean discovery scan");
82 bridgeHandler.stopDiscovery();
87 public Set<@NonNull ThingTypeUID> getSupportedThingTypes() {
88 return SUPPORTED_DEVICE_THING_TYPES_UIDS;
92 public void packetReceived(BasePacket packet) {
93 ERP1Message msg = (ERP1Message) packet;
95 logger.info("EnOcean Package discovered, RORG {}, payload {}, additional {}", msg.getRORG().name(),
96 HexUtils.bytesToHex(msg.getPayload()), HexUtils.bytesToHex(msg.getOptionalPayload()));
98 EEP eep = EEPFactory.buildEEPFromTeachInERP1(msg);
100 logger.debug("Could not build EEP for received package");
104 String enoceanId = HexUtils.bytesToHex(eep.getSenderId());
105 ThingTypeUID thingTypeUID = eep.getThingTypeUID();
106 ThingUID thingUID = new ThingUID(thingTypeUID, bridgeHandler.getThing().getUID(), enoceanId);
108 int senderIdOffset = 0;
109 boolean broadcastMessages = true;
111 // check for bidirectional communication => do not use broadcast in this case
112 if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0]
113 & UTEResponse.CommunicationType_MASK) == UTEResponse.CommunicationType_MASK) {
114 broadcastMessages = false;
117 // if ute => send response if needed
118 if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] & UTEResponse.ResponseNeeded_MASK) == 0) {
119 logger.info("Sending UTE response to {}", enoceanId);
120 senderIdOffset = sendTeachInResponse(msg, enoceanId);
123 // if 4BS teach in variation 3 => send response
124 if ((eep instanceof _4BSMessage) && ((_4BSMessage) eep).isTeachInVariation3Supported()) {
125 logger.info("Sending 4BS teach in variation 3 response to {}", enoceanId);
126 senderIdOffset = sendTeachInResponse(msg, enoceanId);
129 DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID)
130 .withRepresentationProperty(enoceanId).withBridge(bridgeHandler.getThing().getUID());
132 eep.addConfigPropertiesTo(discoveryResultBuilder);
133 discoveryResultBuilder.withProperty(PARAMETER_BROADCASTMESSAGES, broadcastMessages);
134 discoveryResultBuilder.withProperty(PARAMETER_ENOCEANID, enoceanId);
136 if (senderIdOffset > 0) {
137 // advance config with new device id
138 discoveryResultBuilder.withProperty(PARAMETER_SENDERIDOFFSET, senderIdOffset);
141 thingDiscovered(discoveryResultBuilder.build());
143 // As we only support sensors to be teached in, we do not need to send a teach in response => 4bs
144 // bidirectional teach in proc is not supported yet
145 // this is true except for UTE teach in => we always have to send a response here
148 private int sendTeachInResponse(ERP1Message msg, String enoceanId) {
151 offset = bridgeHandler.getNextSenderId(enoceanId);
153 byte[] newSenderId = bridgeHandler.getBaseId();
154 newSenderId[3] += offset;
157 EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId);
158 if (response != null) {
159 bridgeHandler.sendMessage(response.getERP1Message(), null);
160 logger.info("Teach in response for {} with new senderId {} (= offset {}) sent", enoceanId,
161 HexUtils.bytesToHex(newSenderId), offset);
163 logger.warn("Teach in response for enoceanId {} not supported!", enoceanId);
170 public long getSenderIdToListenTo() {
171 // we just want teach in msg, so return zero here