]> git.basschouten.com Git - openhab-addons.git/blob
203218baa75aeed676c39b3431294a5b275c8384
[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.mqtt.homeassistant.internal.component;
14
15 import java.util.List;
16 import java.util.Map;
17 import java.util.TreeMap;
18 import java.util.concurrent.CompletableFuture;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.stream.Collectors;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
25 import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
26 import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
27 import org.openhab.binding.mqtt.generic.utils.FutureCollector;
28 import org.openhab.binding.mqtt.generic.values.Value;
29 import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
30 import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel;
31 import org.openhab.binding.mqtt.homeassistant.internal.HaID;
32 import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory.ComponentConfiguration;
33 import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
34 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
35 import org.openhab.core.thing.ChannelGroupUID;
36 import org.openhab.core.thing.type.ChannelDefinition;
37 import org.openhab.core.thing.type.ChannelGroupDefinition;
38 import org.openhab.core.thing.type.ChannelGroupType;
39 import org.openhab.core.thing.type.ChannelGroupTypeBuilder;
40 import org.openhab.core.thing.type.ChannelGroupTypeUID;
41
42 import com.google.gson.Gson;
43
44 /**
45  * A HomeAssistant component is comparable to a channel group.
46  * It has a name and consists of multiple channels.
47  *
48  * @author David Graeff - Initial contribution
49  * @param <C> Config class derived from {@link AbstractChannelConfiguration}
50  */
51 @NonNullByDefault
52 public abstract class AbstractComponent<C extends AbstractChannelConfiguration> {
53     private static final String JINJA_PREFIX = "JINJA:";
54
55     // Component location fields
56     private final ComponentConfiguration componentConfiguration;
57     protected final ChannelGroupTypeUID channelGroupTypeUID;
58     protected final ChannelGroupUID channelGroupUID;
59     protected final HaID haID;
60
61     // Channels and configuration
62     protected final Map<String, ComponentChannel> channels = new TreeMap<>();
63     // The hash code ({@link String#hashCode()}) of the configuration string
64     // Used to determine if a component has changed.
65     protected final int configHash;
66     protected final String channelConfigurationJson;
67     protected final C channelConfiguration;
68
69     protected boolean configSeen;
70
71     /**
72      * Creates component based on generic configuration and component configuration type.
73      *
74      * @param componentConfiguration generic componentConfiguration with not parsed JSON config
75      * @param clazz target configuration type
76      */
77     public AbstractComponent(ComponentFactory.ComponentConfiguration componentConfiguration, Class<C> clazz) {
78         this.componentConfiguration = componentConfiguration;
79
80         this.channelConfigurationJson = componentConfiguration.getConfigJSON();
81         this.channelConfiguration = componentConfiguration.getConfig(clazz);
82         this.configHash = channelConfigurationJson.hashCode();
83
84         this.haID = componentConfiguration.getHaID();
85
86         String groupId = this.haID.getGroupId(channelConfiguration.getUniqueId());
87
88         this.channelGroupTypeUID = new ChannelGroupTypeUID(MqttBindingConstants.BINDING_ID, groupId);
89         this.channelGroupUID = new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
90
91         this.configSeen = false;
92
93         String availabilityTopic = this.channelConfiguration.getAvailabilityTopic();
94         if (availabilityTopic != null) {
95             String availabilityTemplate = this.channelConfiguration.getAvailabilityTemplate();
96             if (availabilityTemplate != null) {
97                 availabilityTemplate = JINJA_PREFIX + availabilityTemplate;
98             }
99             componentConfiguration.getTracker().addAvailabilityTopic(availabilityTopic,
100                     this.channelConfiguration.getPayloadAvailable(), this.channelConfiguration.getPayloadNotAvailable(),
101                     availabilityTemplate, componentConfiguration.getTransformationServiceProvider());
102         }
103     }
104
105     protected ComponentChannel.Builder buildChannel(String channelID, Value valueState, String label,
106             ChannelStateUpdateListener channelStateUpdateListener) {
107         return new ComponentChannel.Builder(this, channelID, valueState, label, channelStateUpdateListener);
108     }
109
110     public void setConfigSeen() {
111         this.configSeen = true;
112     }
113
114     /**
115      * Subscribes to all state channels of the component and adds all channels to the provided channel type provider.
116      *
117      * @param connection connection to the MQTT broker
118      * @param scheduler thing scheduler
119      * @param timeout channel subscription timeout
120      * @return A future that completes as soon as all subscriptions have been performed. Completes exceptionally on
121      *         errors.
122      */
123     public CompletableFuture<@Nullable Void> start(MqttBrokerConnection connection, ScheduledExecutorService scheduler,
124             int timeout) {
125         return channels.values().stream().map(cChannel -> cChannel.start(connection, scheduler, timeout))
126                 .collect(FutureCollector.allOf());
127     }
128
129     /**
130      * Unsubscribes from all state channels of the component.
131      *
132      * @return A future that completes as soon as all subscriptions removals have been performed. Completes
133      *         exceptionally on errors.
134      */
135     public CompletableFuture<@Nullable Void> stop() {
136         return channels.values().stream().map(ComponentChannel::stop).collect(FutureCollector.allOf());
137     }
138
139     /**
140      * Add all channel types to the channel type provider.
141      *
142      * @param channelTypeProvider The channel type provider
143      */
144     public void addChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
145         channelTypeProvider.setChannelGroupType(getGroupTypeUID(), getType());
146         channels.values().forEach(v -> v.addChannelTypes(channelTypeProvider));
147     }
148
149     /**
150      * Removes all channels from the channel type provider.
151      * Call this if the corresponding Thing handler gets disposed.
152      *
153      * @param channelTypeProvider The channel type provider
154      */
155     public void removeChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
156         channels.values().forEach(v -> v.removeChannelTypes(channelTypeProvider));
157         channelTypeProvider.removeChannelGroupType(getGroupTypeUID());
158     }
159
160     /**
161      * Each HomeAssistant component corresponds to a Channel Group Type.
162      */
163     public ChannelGroupTypeUID getGroupTypeUID() {
164         return channelGroupTypeUID;
165     }
166
167     /**
168      * The unique id of this component.
169      */
170     public ChannelGroupUID getGroupUID() {
171         return channelGroupUID;
172     }
173
174     /**
175      * Component (Channel Group) name.
176      */
177     public String getName() {
178         return channelConfiguration.getName();
179     }
180
181     /**
182      * Each component consists of multiple Channels.
183      */
184     public Map<String, ComponentChannel> getChannelMap() {
185         return channels;
186     }
187
188     /**
189      * Return a components channel. A HomeAssistant MQTT component consists of multiple functions
190      * and those are mapped to one or more channels. The channel IDs are constants within the
191      * derived Component, like the {@link Switch#SWITCH_CHANNEL_ID}.
192      *
193      * @param channelID The channel ID
194      * @return A components channel
195      */
196     public @Nullable ComponentChannel getChannel(String channelID) {
197         return channels.get(channelID);
198     }
199
200     /**
201      * @return Returns the configuration hash value for easy comparison.
202      */
203     public int getConfigHash() {
204         return configHash;
205     }
206
207     /**
208      * Return the channel group type.
209      */
210     public ChannelGroupType getType() {
211         final List<ChannelDefinition> channelDefinitions = channels.values().stream().map(ComponentChannel::type)
212                 .collect(Collectors.toList());
213         return ChannelGroupTypeBuilder.instance(channelGroupTypeUID, getName())
214                 .withChannelDefinitions(channelDefinitions).build();
215     }
216
217     /**
218      * Resets all channel states to state UNDEF. Call this method after the connection
219      * to the MQTT broker got lost.
220      */
221     public void resetState() {
222         channels.values().forEach(ComponentChannel::resetState);
223     }
224
225     /**
226      * Return the channel group definition for this component.
227      */
228     public ChannelGroupDefinition getGroupDefinition() {
229         return new ChannelGroupDefinition(channelGroupUID.getId(), getGroupTypeUID(), getName(), null);
230     }
231
232     public HaID getHaID() {
233         return haID;
234     }
235
236     public String getChannelConfigurationJson() {
237         return channelConfigurationJson;
238     }
239
240     @Nullable
241     public TransformationServiceProvider getTransformationServiceProvider() {
242         return componentConfiguration.getTransformationServiceProvider();
243     }
244
245     public boolean isEnabledByDefault() {
246         return channelConfiguration.isEnabledByDefault();
247     }
248
249     public Gson getGson() {
250         return componentConfiguration.getGson();
251     }
252 }