]> git.basschouten.com Git - openhab-addons.git/blob
55c8dd1dedb9986e1d64465515f685dc968c7960
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.lutron.internal.discovery;
14
15 import static org.openhab.binding.lutron.internal.LutronBindingConstants.*;
16
17 import java.net.InetAddress;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25 import javax.jmdns.ServiceInfo;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.core.config.discovery.DiscoveryResult;
30 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
31 import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.thing.ThingUID;
35 import org.osgi.service.component.annotations.Component;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * The {@link LutronMdnsBridgeDiscoveryService} discovers Lutron Caseta Smart Bridge, Caseta Smart Bridge Pro, RA2
41  * Select Main Repeater, and other Lutron devices on the network using mDNS.
42  *
43  * @author Bob Adair - Initial contribution
44  */
45 @Component
46 @NonNullByDefault
47 public class LutronMdnsBridgeDiscoveryService implements MDNSDiscoveryParticipant {
48
49     // Lutron mDNS service <app>.<protocol>.<servicedomain>
50     private static final String LUTRON_MDNS_SERVICE_TYPE = "_lutron._tcp.local.";
51
52     private static final String PRODFAM_CASETA = "Caseta";
53     private static final String PRODTYP_CASETA_SB = "Smart Bridge";
54     private static final String DEVCLASS_CASETA_SB = "08040100";
55     private static final String PRODTYP_CASETA_SBP2 = "Smart Bridge Pro 2";
56     private static final String DEVCLASS_CASETA_SBP2 = "08050100";
57
58     private static final String PRODFAM_RA2_SELECT = "RA2 Select";
59     private static final String PRODTYP_RA2_SELECT = "Main Repeater";
60     private static final String DEVCLASS_RA2_SELECT = "080E0401";
61
62     private static final String PRODFAM_RA3 = "RadioRA 3";
63     private static final String PRODTYP_RA3 = "Processor";
64     private static final String DEVCLASS_RA3 = "081B0101";
65
66     private static final String DEVCLASS_CONNECT_BRIDGE = "08090301";
67     private static final String DEFAULT_LABEL = "Unknown Lutron bridge";
68
69     private static final Pattern HOSTNAME_REGEX = Pattern.compile("lutron-([0-9a-f]+)\\."); // ex: lutron-01f1529a.local
70
71     private final Logger logger = LoggerFactory.getLogger(LutronMdnsBridgeDiscoveryService.class);
72
73     @Override
74     public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
75         return Collections.singleton(THING_TYPE_IPBRIDGE);
76     }
77
78     @Override
79     public String getServiceType() {
80         return LUTRON_MDNS_SERVICE_TYPE;
81     }
82
83     @Override
84     public @Nullable DiscoveryResult createResult(ServiceInfo service) {
85         if (!service.hasData()) {
86             return null;
87         }
88
89         String nice = service.getNiceTextString();
90         String qualifiedName = service.getQualifiedName();
91
92         InetAddress[] ipAddresses = service.getInetAddresses();
93         String devclass = service.getPropertyString("DEVCLASS");
94         String codever = service.getPropertyString("CODEVER");
95         String macaddr = service.getPropertyString("MACADDR");
96
97         logger.debug("Lutron mDNS bridge discovery notified of Lutron mDNS service: {}", nice);
98         logger.trace("Lutron mDNS service qualifiedName: {}", qualifiedName);
99         logger.trace("Lutron mDNS service ipAddresses: {} ({})", ipAddresses, ipAddresses.length);
100         logger.trace("Lutron mDNS service property DEVCLASS: {}", devclass);
101         logger.trace("Lutron mDNS service property CODEVER: {}", codever);
102         logger.trace("Lutron mDNS service property MACADDR: {}", macaddr);
103
104         Map<String, Object> properties = new HashMap<>();
105         String label = DEFAULT_LABEL;
106
107         if (ipAddresses.length < 1) {
108             return null;
109         }
110         if (ipAddresses.length > 1) {
111             logger.debug("Multiple addresses found for discovered Lutron device. Using only the first.");
112         }
113         properties.put(HOST, ipAddresses[0].getHostAddress());
114
115         String bridgeHostName = ipAddresses[0].getHostName();
116         logger.debug("Lutron mDNS bridge hostname: {}", bridgeHostName);
117
118         if (DEVCLASS_CASETA_SB.equals(devclass)) {
119             properties.put(PROPERTY_PRODFAM, PRODFAM_CASETA);
120             properties.put(PROPERTY_PRODTYP, PRODTYP_CASETA_SB);
121             label = PRODFAM_CASETA + " " + PRODTYP_CASETA_SB;
122         } else if (DEVCLASS_CASETA_SBP2.equals(devclass)) {
123             properties.put(PROPERTY_PRODFAM, PRODFAM_CASETA);
124             properties.put(PROPERTY_PRODTYP, PRODTYP_CASETA_SBP2);
125             label = PRODFAM_CASETA + " " + PRODTYP_CASETA_SBP2;
126         } else if (DEVCLASS_RA2_SELECT.equals(devclass)) {
127             properties.put(PROPERTY_PRODFAM, PRODFAM_RA2_SELECT);
128             properties.put(PROPERTY_PRODTYP, PRODTYP_RA2_SELECT);
129             label = PRODFAM_RA2_SELECT + " " + PRODTYP_RA2_SELECT;
130         } else if (DEVCLASS_RA3.equals(devclass)) {
131             properties.put(PROPERTY_PRODFAM, PRODFAM_RA3);
132             properties.put(PROPERTY_PRODTYP, PRODTYP_RA3);
133             label = PRODFAM_RA3 + " " + PRODTYP_RA3;
134         } else if (DEVCLASS_CONNECT_BRIDGE.equals(devclass)) {
135             logger.debug("Lutron Connect Bridge discovered. Ignoring.");
136             return null;
137         } else {
138             logger.info("Lutron device with unknown DEVCLASS discovered via mDNS: {}. Configure device manually.",
139                     devclass);
140             return null; // Exit if service has unknown DEVCLASS
141         }
142
143         if (!bridgeHostName.equals(ipAddresses[0].getHostAddress())) {
144             label = label + " " + bridgeHostName;
145         }
146
147         if (codever != null) {
148             properties.put(Thing.PROPERTY_FIRMWARE_VERSION, codever);
149         }
150
151         if (macaddr != null) {
152             properties.put(Thing.PROPERTY_MAC_ADDRESS, macaddr);
153         }
154
155         String sn = getSerial(service);
156         if (sn != null) {
157             logger.trace("Lutron mDNS bridge serial number: {}", sn);
158             properties.put(SERIAL_NUMBER, sn);
159         } else {
160             logger.debug("Unable to determine serial number of discovered Lutron bridge device.");
161             return null;
162         }
163
164         ThingUID uid = getThingUID(service);
165         if (uid != null) {
166             DiscoveryResult result = DiscoveryResultBuilder.create(uid).withLabel(label).withProperties(properties)
167                     .withRepresentationProperty(SERIAL_NUMBER).build();
168             logger.debug("Discovered Lutron bridge device via mDNS {}", uid);
169             return result;
170         } else {
171             logger.trace("Failed to create uid for discovered Lutron bridge device");
172             return null;
173         }
174     }
175
176     @Override
177     public @Nullable ThingUID getThingUID(ServiceInfo service) {
178         String serial = getSerial(service);
179         String devclass = service.getPropertyString("DEVCLASS");
180         if (serial == null) {
181             return null;
182         } else {
183             if (DEVCLASS_CASETA_SB.equals(devclass)) {
184                 return new ThingUID(THING_TYPE_LEAPBRIDGE, serial);
185             } else {
186                 return new ThingUID(THING_TYPE_IPBRIDGE, serial);
187             }
188         }
189     }
190
191     /**
192      * Returns the device serial number for the mDNS service by extracting it from the hostname.
193      * Used as unique thing representation property.
194      *
195      * @param service Lutron mDNS service
196      * @return String containing serial number, or null if it cannot be determined
197      */
198     private @Nullable String getSerial(ServiceInfo service) {
199         InetAddress[] ipAddresses = service.getInetAddresses();
200         if (ipAddresses.length < 1) {
201             return null;
202         }
203         Matcher matcher = HOSTNAME_REGEX.matcher(ipAddresses[0].getHostName());
204         boolean matched = matcher.find();
205         String serialnum = null;
206
207         if (matched) {
208             serialnum = matcher.group(1);
209         }
210         if (matched && serialnum != null && !serialnum.isEmpty()) {
211             return serialnum;
212         } else {
213             return null;
214         }
215     }
216 }