Skip to content

[danfossairunit] Use serial number as valid Thing ID in discovery #18414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;

Expand All @@ -36,32 +37,38 @@
@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
public synchronized void connect() throws IOException {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@

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;
Expand All @@ -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);

Expand Down Expand Up @@ -112,15 +116,15 @@ private synchronized void discover() {

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);
socket.send(sendPacket);
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 {
Expand All @@ -133,20 +137,40 @@ private void sendBroadcastToDiscoverThing(DatagramSocket socket, InetAddress bro
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);

logger.debug("Adding a new Danfoss Air Unit CCM '{}' to inbox", host);
if (serialNumber == null) {
logger.debug("Unable to get serial number from Danfoss Air Unit CCM '{}'", host);
continue;
}

ThingUID uid = new ThingUID(THING_TYPE_AIRUNIT, String.valueOf(receivePacket.getAddress().hashCode()));
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);

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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
@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;
Expand Down Expand Up @@ -99,7 +98,7 @@ public void initialize() {
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down