]> git.basschouten.com Git - openhab-addons.git/blob
1b1e15106fff4d64d27c1617a26ed33c05317bea
[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 package org.openhab.binding.mqtt.homeassistant.internal;
14
15 import java.util.ArrayList;
16 import java.util.Collection;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.core.config.core.Configuration;
22 import org.openhab.core.util.UIDUtils;
23
24 /**
25  * HomeAssistant MQTT components use a specific MQTT topic layout,
26  * starting with a base prefix (usually "homeassistant"),
27  * followed by the component id, an optional node id and the object id.
28  *
29  * This helper class can split up an MQTT topic into such parts.
30  * <p>
31  * Implementation note: This is an immutable class.
32  *
33  * @author David Graeff - Initial contribution
34  */
35 @NonNullByDefault
36 public class HaID {
37     public final String baseTopic;
38     public final String component;
39     public final String nodeID;
40     public final String objectID;
41
42     private final String topic;
43
44     /**
45      * Creates a {@link HaID} object for a given HomeAssistant MQTT topic.
46      *
47      * @param mqttTopic A topic like "homeassistant/binary_sensor/garden/config" or
48      *            "homeassistant/binary_sensor/0/garden/config"
49      */
50     public HaID(String mqttTopic) {
51         String[] strings = mqttTopic.split("/");
52         if (strings.length < 4 || strings.length > 5) {
53             throw new IllegalArgumentException("MQTT topic not a HomeAssistant topic (wrong length)!");
54         }
55         if (!"config".equals(strings[strings.length - 1])) {
56             throw new IllegalArgumentException("MQTT topic not a HomeAssistant topic ('config' missing)!");
57         }
58
59         baseTopic = strings[0];
60         component = strings[1];
61
62         if (strings.length == 5) {
63             nodeID = strings[2];
64             objectID = strings[3];
65         } else {
66             nodeID = "";
67             objectID = strings[2];
68         }
69
70         this.topic = createTopic(this);
71     }
72
73     public HaID() {
74         this("", "", "", "");
75     }
76
77     /**
78      * Creates a {@link HaID} by providing all components separately.
79      *
80      * @param baseTopic The base topic. Usually "homeassistant".
81      * @param objectID The object ID
82      * @param nodeID The node ID (can be the empty string)
83      * @param component The component ID
84      */
85     private HaID(String baseTopic, String objectID, String nodeID, String component) {
86         this.baseTopic = baseTopic;
87         this.objectID = objectID;
88         this.nodeID = nodeID;
89         this.component = component;
90         this.topic = createTopic(this);
91     }
92
93     private static final String createTopic(HaID id) {
94         StringBuilder str = new StringBuilder();
95         str.append(id.baseTopic).append('/').append(id.component).append('/');
96         if (StringUtils.isNotBlank(id.nodeID)) {
97             str.append(id.nodeID).append('/');
98         }
99         str.append(id.objectID).append('/');
100         return str.toString();
101     }
102
103     /**
104      * Extract the HaID information from a channel configuration.
105      * <p>
106      * <code>objectid</code>, <code>nodeid</code>, and <code>component</code> values are fetched from the configuration.
107      *
108      * @param baseTopic
109      * @param config
110      * @return newly created HaID
111      */
112     public static HaID fromConfig(String baseTopic, Configuration config) {
113         String component = (String) config.get("component");
114         String nodeID = (String) config.getProperties().getOrDefault("nodeid", "");
115         String objectID = (String) config.get("objectid");
116         return new HaID(baseTopic, objectID, nodeID, component);
117     }
118
119     /**
120      * Add the HaID information to a channel configuration.
121      * <p>
122      * <code>objectid</code>, <code>nodeid</code>, and <code>component</code> values are added to the configuration.
123      *
124      * @param config
125      * @return the modified configuration
126      */
127     public Configuration toConfig(Configuration config) {
128         config.put("objectid", objectID);
129         config.put("nodeid", nodeID);
130         config.put("component", component);
131         return config;
132     }
133
134     /**
135      * Extract the HaID information from a thing configuration.
136      * <p>
137      * <code>basetpoic</code> and <code>objectid</code> are taken from the configuration.
138      * The <code>objectid</code> string may be in the form <code>nodeid/objectid</code>.
139      * <p>
140      * The <code>component</code> component in the resulting HaID will be set to <code>+</code>.
141      * This enables the HaID to be used as an mqtt subscription topic.
142      *
143      * @param config
144      * @return newly created HaID
145      */
146     public static Collection<HaID> fromConfig(HandlerConfiguration config) {
147         Collection<HaID> result = new ArrayList<>();
148
149         for (String topic : config.topics) {
150             String[] parts = topic.split("/");
151
152             switch (parts.length) {
153                 case 2:
154                     result.add(new HaID(config.basetopic, parts[1], "", parts[0]));
155                     break;
156                 case 3:
157                     result.add(new HaID(config.basetopic, parts[2], parts[1], parts[0]));
158                     break;
159                 default:
160                     throw new IllegalArgumentException(
161                             "Bad configuration. topic must be <component>/<objectId> or <component>/<nodeId>/<objectId>!");
162             }
163         }
164         return result;
165     }
166
167     /**
168      * Return the topic to put into the HandlerConfiguration for this component.
169      * <p>
170      * <code>objectid</code> in the thing configuration will be
171      * <code>nodeID/objectID<code> from the HaID, if <code>nodeID</code> is not empty.
172      * <p>
173      *
174      * @return the short topic.
175      */
176     public String toShortTopic() {
177         String objectID = this.objectID;
178         if (StringUtils.isNotBlank(nodeID)) {
179             objectID = nodeID + "/" + objectID;
180         }
181
182         return component + "/" + objectID;
183     }
184
185     /**
186      * The default group id is the unique_id of the component, given in the config-json.
187      * If the unique id is not set, then a fallback is constructed from the HaID information.
188      *
189      * @return group id
190      */
191     public String getGroupId(@Nullable final String uniqueId) {
192         String result = uniqueId;
193
194         // the null test is only here so the compile knows, result is not null afterwards
195         if (result == null || StringUtils.isBlank(result)) {
196             StringBuilder str = new StringBuilder();
197
198             if (StringUtils.isNotBlank(nodeID)) {
199                 str.append(nodeID).append('_');
200             }
201             str.append(objectID).append('_').append(component);
202             result = str.toString();
203         }
204
205         return UIDUtils.encode(result);
206     }
207
208     /**
209      * Return a topic, which can be used for a mqtt subscription.
210      * Defined values for suffix are:
211      * <ul>
212      * <li>config</li>
213      * <li>state</li>
214      * </ul>
215      *
216      * @return fallback group id
217      */
218     public String getTopic(String suffix) {
219         return topic + suffix;
220     }
221
222     @Override
223     public int hashCode() {
224         final int prime = 31;
225         int result = 1;
226         result = prime * result + baseTopic.hashCode();
227         result = prime * result + component.hashCode();
228         result = prime * result + nodeID.hashCode();
229         result = prime * result + objectID.hashCode();
230         return result;
231     }
232
233     @Override
234     public boolean equals(@Nullable Object obj) {
235         if (this == obj) {
236             return true;
237         }
238         if (obj == null) {
239             return false;
240         }
241         if (getClass() != obj.getClass()) {
242             return false;
243         }
244         HaID other = (HaID) obj;
245         if (!baseTopic.equals(other.baseTopic)) {
246             return false;
247         }
248         if (!component.equals(other.component)) {
249             return false;
250         }
251         if (!nodeID.equals(other.nodeID)) {
252             return false;
253         }
254         if (!objectID.equals(other.objectID)) {
255             return false;
256         }
257         return true;
258     }
259
260     @Override
261     public String toString() {
262         return topic;
263     }
264 }