Improve discovery (#18414)

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
Jacob Laursen
2025-03-22 22:08:01 +01:00
committed by GitHub
parent 5e648a72ee
commit 8ef6b6a964
5 changed files with 58 additions and 22 deletions
@@ -34,4 +34,7 @@ public class DanfossAirUnitBindingConstants {
// The thing type as a set
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AIRUNIT);
// The thing type configuration parameters
public static final String PARAMETER_HOST = "host";
}
@@ -18,6 +18,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;
@@ -36,20 +37,26 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault
public class DanfossAirUnitCommunicationController implements CommunicationController {
private static final int SOCKET_TIMEOUT_MILLISECONDS = 5_000;
private static final int TCP_PORT = 30046;
private static final int READ_TIMEOUT_MILLISECONDS = 5_000;
private static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 5_000;
private final Logger logger = LoggerFactory.getLogger(DanfossAirUnitCommunicationController.class);
private final InetAddress inetAddr;
private final int port;
private final int connectTimeoutMilliseconds;
private boolean connected = false;
private @Nullable Socket socket;
private @Nullable OutputStream outputStream;
private @Nullable InputStream inputStream;
public DanfossAirUnitCommunicationController(InetAddress inetAddr, int port) {
public DanfossAirUnitCommunicationController(InetAddress inetAddr) {
this(inetAddr, DEFAULT_CONNECT_TIMEOUT_MILLISECONDS);
}
public DanfossAirUnitCommunicationController(InetAddress inetAddr, int connectTimeoutMilliseconds) {
this.inetAddr = inetAddr;
this.port = port;
this.connectTimeoutMilliseconds = connectTimeoutMilliseconds;
}
@Override
@@ -57,11 +64,11 @@ public class DanfossAirUnitCommunicationController implements CommunicationContr
if (connected) {
return;
}
Socket localSocket = new Socket(inetAddr, port);
localSocket.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS);
this.outputStream = localSocket.getOutputStream();
this.inputStream = localSocket.getInputStream();
this.socket = localSocket;
Socket socket = this.socket = new Socket();
socket.connect(new InetSocketAddress(inetAddr, TCP_PORT), connectTimeoutMilliseconds);
socket.setSoTimeout(READ_TIMEOUT_MILLISECONDS);
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
connected = true;
}
@@ -29,12 +29,15 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.danfossairunit.internal.DanfossAirUnit;
import org.openhab.binding.danfossairunit.internal.DanfossAirUnitCommunicationController;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Activate;
@@ -57,6 +60,7 @@ public class DanfossAirUnitDiscoveryService extends AbstractDiscoveryService {
private static final byte[] DISCOVER_SEND = { 0x0c, 0x00, 0x30, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13 };
private static final byte[] DISCOVER_RECEIVE = { 0x0d, 0x00, 0x07, 0x00, 0x02, 0x02, 0x00 };
private static final int TIMEOUT_IN_SECONDS = 15;
private static final int SOCKET_TIMEOUT_MILLISECONDS = 500;
private final Logger logger = LoggerFactory.getLogger(DanfossAirUnitDiscoveryService.class);
@@ -112,7 +116,7 @@ public class DanfossAirUnitDiscoveryService extends AbstractDiscoveryService {
private void sendBroadcastToDiscoverThing(DatagramSocket socket, InetAddress broadcastAddress) throws IOException {
socket.setBroadcast(true);
socket.setSoTimeout(500);
socket.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS);
// send discover
byte[] sendBuffer = DISCOVER_SEND;
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, broadcastAddress, BROADCAST_PORT);
@@ -120,7 +124,7 @@ public class DanfossAirUnitDiscoveryService extends AbstractDiscoveryService {
logger.debug("Discover message sent");
// wait for responses
while (true) {
while (!Thread.interrupted()) {
byte[] receiveBuffer = new byte[7];
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
try {
@@ -133,20 +137,40 @@ public class DanfossAirUnitDiscoveryService extends AbstractDiscoveryService {
if (Arrays.equals(data, DISCOVER_RECEIVE)) {
logger.debug("Discover received correct response");
String host = receivePacket.getAddress().getHostName();
Map<String, Object> properties = new HashMap<>();
properties.put("host", host);
InetAddress address = receivePacket.getAddress();
String host = address.getHostName();
String serialNumber = getSerialNumber(address);
if (serialNumber == null) {
logger.debug("Unable to get serial number from Danfoss Air Unit CCM '{}'", host);
continue;
}
Map<String, Object> properties = new HashMap<>(2);
properties.put(PARAMETER_HOST, host);
properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNumber);
logger.debug("Adding a new Danfoss Air Unit CCM '{}' to inbox", host);
ThingUID uid = new ThingUID(THING_TYPE_AIRUNIT, String.valueOf(receivePacket.getAddress().hashCode()));
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withRepresentationProperty("host")
.withProperties(properties).withLabel("@text/discovery.danfossairunit.label").build();
DiscoveryResult result = DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_AIRUNIT, serialNumber))
.withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER).withProperties(properties)
.withLabel("@text/discovery.danfossairunit.label").build();
thingDiscovered(result);
logger.debug("Thing discovered '{}'", result);
}
}
}
private @Nullable String getSerialNumber(InetAddress address) {
var controller = new DanfossAirUnitCommunicationController(address, SOCKET_TIMEOUT_MILLISECONDS);
var unit = new DanfossAirUnit(controller);
try {
return unit.getUnitSerialNumber();
} catch (IOException e) {
return null;
} finally {
controller.disconnect();
}
}
}
@@ -52,7 +52,6 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault
public class DanfossAirUnitHandler extends BaseThingHandler {
private static final int TCP_PORT = 30046;
private static final int POLLING_INTERVAL_SECONDS = 5;
private final Logger logger = LoggerFactory.getLogger(DanfossAirUnitHandler.class);
private @NonNullByDefault({}) DanfossAirUnitConfiguration config;
@@ -99,7 +98,7 @@ public class DanfossAirUnitHandler extends BaseThingHandler {
valueCache = new ValueCache(config.updateUnchangedValuesEveryMillis);
try {
var localCommunicationController = new DanfossAirUnitCommunicationController(
InetAddress.getByName(config.host), TCP_PORT);
InetAddress.getByName(config.host));
this.communicationController = localCommunicationController;
this.airUnit = new DanfossAirUnit(localCommunicationController);
startPolling();
@@ -16,11 +16,14 @@
<channel-group id="recuperator" typeId="recuperator"/>
<channel-group id="service" typeId="service"/>
</channel-groups>
<properties>
<property name="vendor">Danfoss</property>
<property name="thingTypeVersion">2</property>
</properties>
<representation-property>host</representation-property>
<representation-property>serialNumber</representation-property>
<config-description>
<parameter name="host" type="text" required="true">
<label>Host</label>