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