]> git.basschouten.com Git - openhab-addons.git/blob
ebd2b155495b2913ff0701631e0ad755896dd187
[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, Caseta Smart Bridge Pro, RA2
42  * Select Main 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_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";
58
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";
62
63     private static final String DEVCLASS_CONNECT_BRIDGE = "08090301";
64     private static final String DEFAULT_LABEL = "Unknown Lutron bridge";
65
66     private static final Pattern HOSTNAME_REGEX = Pattern.compile("lutron-([0-9a-f]+)\\."); // ex: lutron-01f1529a.local
67
68     private final Logger logger = LoggerFactory.getLogger(LutronMdnsBridgeDiscoveryService.class);
69
70     @Override
71     public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
72         return Collections.singleton(THING_TYPE_IPBRIDGE);
73     }
74
75     @Override
76     public String getServiceType() {
77         return LUTRON_MDNS_SERVICE_TYPE;
78     }
79
80     @Override
81     public @Nullable DiscoveryResult createResult(ServiceInfo service) {
82         if (!service.hasData()) {
83             return null;
84         }
85
86         String nice = service.getNiceTextString();
87         String qualifiedName = service.getQualifiedName();
88
89         InetAddress[] ipAddresses = service.getInetAddresses();
90         String devclass = service.getPropertyString("DEVCLASS");
91         String codever = service.getPropertyString("CODEVER");
92         String macaddr = service.getPropertyString("MACADDR");
93
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);
100
101         Map<String, Object> properties = new HashMap<>();
102         String label = DEFAULT_LABEL;
103
104         if (ipAddresses.length < 1) {
105             return null;
106         }
107         if (ipAddresses.length > 1) {
108             logger.debug("Multiple addresses found for discovered Lutron device. Using only the first.");
109         }
110         properties.put(HOST, ipAddresses[0].getHostAddress());
111
112         String bridgeHostName = ipAddresses[0].getHostName();
113         logger.debug("Lutron mDNS bridge hostname: {}", bridgeHostName);
114
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.");
129             return null;
130         } else {
131             logger.info("Lutron device with unknown DEVCLASS discovered via mDNS: {}. Configure device manually.",
132                     devclass);
133             return null; // Exit if service has unknown DEVCLASS
134         }
135
136         if (!bridgeHostName.equals(ipAddresses[0].getHostAddress())) {
137             label = label + " " + bridgeHostName;
138         }
139
140         if (codever != null) {
141             properties.put(Thing.PROPERTY_FIRMWARE_VERSION, codever);
142         }
143
144         if (macaddr != null) {
145             properties.put(Thing.PROPERTY_MAC_ADDRESS, macaddr);
146         }
147
148         String sn = getSerial(service);
149         if (sn != null) {
150             logger.trace("Lutron mDNS bridge serial number: {}", sn);
151             properties.put(SERIAL_NUMBER, sn);
152         } else {
153             logger.debug("Unable to determine serial number of discovered Lutron bridge device.");
154             return null;
155         }
156
157         ThingUID uid = getThingUID(service);
158         if (uid != null) {
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);
162             return result;
163         } else {
164             logger.trace("Failed to create uid for discovered Lutron bridge device");
165             return null;
166         }
167     }
168
169     @Override
170     public @Nullable ThingUID getThingUID(ServiceInfo service) {
171         String serial = getSerial(service);
172         String devclass = service.getPropertyString("DEVCLASS");
173         if (serial == null) {
174             return null;
175         } else {
176             if (DEVCLASS_CASETA_SB.equals(devclass)) {
177                 return new ThingUID(THING_TYPE_LEAPBRIDGE, serial);
178             } else {
179                 return new ThingUID(THING_TYPE_IPBRIDGE, serial);
180             }
181         }
182     }
183
184     /**
185      * Returns the device serial number for the mDNS service by extracting it from the hostname.
186      * Used as unique thing representation property.
187      *
188      * @param service Lutron mDNS service
189      * @return String containing serial number, or null if it cannot be determined
190      */
191     private @Nullable String getSerial(ServiceInfo service) {
192         InetAddress[] ipAddresses = service.getInetAddresses();
193         if (ipAddresses.length < 1) {
194             return null;
195         }
196         Matcher matcher = HOSTNAME_REGEX.matcher(ipAddresses[0].getHostName());
197         boolean matched = matcher.find();
198         String serialnum = null;
199
200         if (matched) {
201             serialnum = matcher.group(1);
202         }
203         if (matched && serialnum != null && !serialnum.isEmpty()) {
204             return serialnum;
205         } else {
206             return null;
207         }
208     }
209 }