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
14 package org.openhab.binding.lutron.internal.discovery;
16 import static org.openhab.binding.lutron.internal.LutronBindingConstants.*;
18 import java.net.InetAddress;
19 import java.util.Collections;
20 import java.util.HashMap;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
26 import javax.jmdns.ServiceInfo;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.core.config.discovery.DiscoveryResult;
31 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
32 import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingTypeUID;
35 import org.openhab.core.thing.ThingUID;
36 import org.osgi.service.component.annotations.Component;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link LutronMdnsBridgeDiscoveryService} discovers Lutron Caseta Smart Bridge, Caseta Smart Bridge Pro, RA2
42 * Select Main Repeater, and other Lutron devices on the network using mDNS.
44 * @author Bob Adair - Initial contribution
48 public class LutronMdnsBridgeDiscoveryService implements MDNSDiscoveryParticipant {
50 // Lutron mDNS service <app>.<protocol>.<servicedomain>
51 private static final String LUTRON_MDNS_SERVICE_TYPE = "_lutron._tcp.local.";
53 private static final String PRODFAM_CASETA = "Caseta";
54 private static final String PRODTYP_CASETA_SB = "Smart Bridge";
55 private static final String DEVCLASS_CASETA_SB = "08040100";
56 private static final String PRODTYP_CASETA_SBP2 = "Smart Bridge Pro 2";
57 private static final String DEVCLASS_CASETA_SBP2 = "08050100";
59 private static final String PRODFAM_RA2_SELECT = "RA2 Select";
60 private static final String PRODTYP_RA2_SELECT = "Main Repeater";
61 private static final String DEVCLASS_RA2_SELECT = "080E0401";
63 private static final String DEVCLASS_CONNECT_BRIDGE = "08090301";
64 private static final String DEFAULT_LABEL = "Unknown Lutron bridge";
66 private static final Pattern HOSTNAME_REGEX = Pattern.compile("lutron-([0-9a-f]+)\\."); // ex: lutron-01f1529a.local
68 private final Logger logger = LoggerFactory.getLogger(LutronMdnsBridgeDiscoveryService.class);
71 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
72 return Collections.singleton(THING_TYPE_IPBRIDGE);
76 public String getServiceType() {
77 return LUTRON_MDNS_SERVICE_TYPE;
81 public @Nullable DiscoveryResult createResult(ServiceInfo service) {
82 if (!service.hasData()) {
86 String nice = service.getNiceTextString();
87 String qualifiedName = service.getQualifiedName();
89 InetAddress[] ipAddresses = service.getInetAddresses();
90 String devclass = service.getPropertyString("DEVCLASS");
91 String codever = service.getPropertyString("CODEVER");
92 String macaddr = service.getPropertyString("MACADDR");
94 logger.debug("Lutron mDNS bridge discovery notified of Lutron mDNS service: {}", nice);
95 logger.trace("Lutron mDNS service qualifiedName: {}", qualifiedName);
96 logger.trace("Lutron mDNS service ipAddresses: {} ({})", ipAddresses, ipAddresses.length);
97 logger.trace("Lutron mDNS service property DEVCLASS: {}", devclass);
98 logger.trace("Lutron mDNS service property CODEVER: {}", codever);
99 logger.trace("Lutron mDNS service property MACADDR: {}", macaddr);
101 Map<String, Object> properties = new HashMap<>();
102 String label = DEFAULT_LABEL;
104 if (ipAddresses.length < 1) {
107 if (ipAddresses.length > 1) {
108 logger.debug("Multiple addresses found for discovered Lutron device. Using only the first.");
110 properties.put(HOST, ipAddresses[0].getHostAddress());
112 String bridgeHostName = ipAddresses[0].getHostName();
113 logger.debug("Lutron mDNS bridge hostname: {}", bridgeHostName);
115 if (DEVCLASS_CASETA_SB.equals(devclass)) {
116 properties.put(PROPERTY_PRODFAM, PRODFAM_CASETA);
117 properties.put(PROPERTY_PRODTYP, PRODTYP_CASETA_SB);
118 label = PRODFAM_CASETA + " " + PRODTYP_CASETA_SB;
119 } else if (DEVCLASS_CASETA_SBP2.equals(devclass)) {
120 properties.put(PROPERTY_PRODFAM, PRODFAM_CASETA);
121 properties.put(PROPERTY_PRODTYP, PRODTYP_CASETA_SBP2);
122 label = PRODFAM_CASETA + " " + PRODTYP_CASETA_SBP2;
123 } else if (DEVCLASS_RA2_SELECT.equals(devclass)) {
124 properties.put(PROPERTY_PRODFAM, PRODFAM_RA2_SELECT);
125 properties.put(PROPERTY_PRODTYP, PRODTYP_RA2_SELECT);
126 label = PRODFAM_RA2_SELECT + " " + PRODTYP_RA2_SELECT;
127 } else if (DEVCLASS_CONNECT_BRIDGE.equals(devclass)) {
128 logger.debug("Lutron Connect Bridge discovered. Ignoring.");
131 logger.info("Lutron device with unknown DEVCLASS discovered via mDNS: {}. Configure device manually.",
133 return null; // Exit if service has unknown DEVCLASS
136 if (!bridgeHostName.equals(ipAddresses[0].getHostAddress())) {
137 label = label + " " + bridgeHostName;
140 if (codever != null) {
141 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, codever);
144 if (macaddr != null) {
145 properties.put(Thing.PROPERTY_MAC_ADDRESS, macaddr);
148 String sn = getSerial(service);
150 logger.trace("Lutron mDNS bridge serial number: {}", sn);
151 properties.put(SERIAL_NUMBER, sn);
153 logger.debug("Unable to determine serial number of discovered Lutron bridge device.");
157 ThingUID uid = getThingUID(service);
159 DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(label).withProperties(properties)
160 .withRepresentationProperty(SERIAL_NUMBER).build();
161 logger.debug("Discovered Lutron bridge device via mDNS {}", uid);
164 logger.trace("Failed to create uid for discovered Lutron bridge device");
170 public @Nullable ThingUID getThingUID(ServiceInfo service) {
171 String serial = getSerial(service);
172 String devclass = service.getPropertyString("DEVCLASS");
173 if (serial == null) {
176 if (DEVCLASS_CASETA_SB.equals(devclass)) {
177 return new ThingUID(THING_TYPE_LEAPBRIDGE, serial);
179 return new ThingUID(THING_TYPE_IPBRIDGE, serial);
185 * Returns the device serial number for the mDNS service by extracting it from the hostname.
186 * Used as unique thing representation property.
188 * @param service Lutron mDNS service
189 * @return String containing serial number, or null if it cannot be determined
191 private @Nullable String getSerial(ServiceInfo service) {
192 InetAddress[] ipAddresses = service.getInetAddresses();
193 if (ipAddresses.length < 1) {
196 Matcher matcher = HOSTNAME_REGEX.matcher(ipAddresses[0].getHostName());
197 boolean matched = matcher.find();
198 String serialnum = null;
201 serialnum = matcher.group(1);
203 if (matched && serialnum != null && !serialnum.isEmpty()) {