2 * Copyright (c) 2010-2023 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.mqtt.ruuvigateway.internal.discovery;
16 import static org.openhab.binding.mqtt.ruuvigateway.internal.RuuviGatewayBindingConstants.*;
18 import java.util.HashMap;
20 import java.util.function.Predicate;
21 import java.util.regex.Pattern;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.openhab.binding.mqtt.discovery.AbstractMQTTDiscovery;
25 import org.openhab.binding.mqtt.discovery.MQTTTopicDiscoveryService;
26 import org.openhab.binding.mqtt.ruuvigateway.internal.RuuviGatewayBindingConstants;
27 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
28 import org.openhab.core.config.discovery.DiscoveryService;
29 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingUID;
32 import org.osgi.service.component.annotations.Activate;
33 import org.osgi.service.component.annotations.Component;
34 import org.osgi.service.component.annotations.Reference;
37 * The {@link RuuviGatewayDiscoveryService} is responsible for finding Ruuvi Tag Sensors
38 * and setting them up for the handlers.
40 * @author Matthew Skinner - Initial contribution
41 * @author Sami Salonen - Adaptation to Ruuvi Gateway
43 @Component(service = DiscoveryService.class, configurationPid = "discovery.mqttruuvigateway")
45 public class RuuviGatewayDiscoveryService extends AbstractMQTTDiscovery {
46 protected final MQTTTopicDiscoveryService discoveryService;
48 private static final Predicate<String> HEX_PATTERN_CHECKER = Pattern
49 .compile("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$").asMatchPredicate();
52 public RuuviGatewayDiscoveryService(@Reference MQTTTopicDiscoveryService discoveryService) {
53 super(SUPPORTED_THING_TYPES_UIDS, 3, true, BASE_TOPIC + "#");
54 this.discoveryService = discoveryService;
58 protected MQTTTopicDiscoveryService getDiscoveryService() {
59 return discoveryService;
63 public void receivedMessage(ThingUID connectionBridge, MqttBrokerConnection connection, String topic,
66 if (topic.startsWith(BASE_TOPIC)) {
67 String cutTopic = topic.replace(BASE_TOPIC, "");
68 int index = cutTopic.lastIndexOf("/");
69 if (index != -1) // -1 means "not found"
71 String tagMacAddress = cutTopic.substring(index + 1);
72 if (looksLikeMac(tagMacAddress)) {
73 publishDevice(connectionBridge, connection, topic, tagMacAddress);
79 void publishDevice(ThingUID connectionBridge, MqttBrokerConnection connection, String topic, String tagMacAddress) {
80 Map<String, Object> properties = new HashMap<>();
81 String thingID = tagMacAddress.toLowerCase().replaceAll("[:-]", "");
82 String normalizedTagID = normalizedTagID(tagMacAddress);
83 properties.put(RuuviGatewayBindingConstants.CONFIGURATION_PROPERTY_TOPIC, topic);
84 properties.put(RuuviGatewayBindingConstants.PROPERTY_TAG_ID, normalizedTagID);
85 properties.put(Thing.PROPERTY_VENDOR, "Ruuvi Innovations Ltd (Oy)");
87 // Discovered things are identified with their topic name, in case of having pathological case
88 // where we find multiple tags with same mac address (e.g. ruuvi/gw1/mac1 and ruuvi/gw2/mac1)
89 thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_BEACON, connectionBridge, thingID))
90 .withProperties(properties)
91 .withRepresentationProperty(RuuviGatewayBindingConstants.CONFIGURATION_PROPERTY_TOPIC)
92 .withBridge(connectionBridge).withLabel("MQTT Ruuvi Tag " + normalizedTagID).build());
96 public void topicVanished(ThingUID connectionBridge, MqttBrokerConnection connection, String topic) {
99 private boolean looksLikeMac(String topic) {
100 return HEX_PATTERN_CHECKER.test(topic);
103 private static String normalizedTagID(String mac) {
104 String nondelimited = mac.toUpperCase().replaceAll("[:-]", "");
105 assert nondelimited.length() == 12; // Invariant: method to be used only with valid Ruuvi MACs
106 return nondelimited.subSequence(0, 2) + ":" + nondelimited.subSequence(2, 4) + ":"
107 + nondelimited.subSequence(4, 6) + ":" + nondelimited.subSequence(6, 8) + ":"
108 + nondelimited.subSequence(8, 10) + ":" + nondelimited.subSequence(10, 12);