2 * Copyright (c) 2010-2020 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
13 package org.openhab.binding.mqtt.homeassistant.internal;
15 import java.util.List;
17 import java.util.TreeMap;
18 import java.util.concurrent.CompletableFuture;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.stream.Collectors;
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.utils.FutureCollector;
27 import org.openhab.binding.mqtt.generic.values.Value;
28 import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants;
29 import org.openhab.binding.mqtt.homeassistant.internal.CFactory.ComponentConfiguration;
30 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
31 import org.openhab.core.thing.ChannelGroupUID;
32 import org.openhab.core.thing.type.ChannelDefinition;
33 import org.openhab.core.thing.type.ChannelGroupDefinition;
34 import org.openhab.core.thing.type.ChannelGroupType;
35 import org.openhab.core.thing.type.ChannelGroupTypeBuilder;
36 import org.openhab.core.thing.type.ChannelGroupTypeUID;
39 * A HomeAssistant component is comparable to a channel group.
40 * It has a name and consists of multiple channels.
42 * @author David Graeff - Initial contribution
43 * @param <C> Config class derived from {@link BaseChannelConfiguration}
46 public abstract class AbstractComponent<C extends BaseChannelConfiguration> {
47 // Component location fields
48 private final ComponentConfiguration componentConfiguration;
49 protected final ChannelGroupTypeUID channelGroupTypeUID;
50 protected final ChannelGroupUID channelGroupUID;
51 protected final HaID haID;
53 // Channels and configuration
54 protected final Map<String, CChannel> channels = new TreeMap<>();
55 // The hash code ({@link String#hashCode()}) of the configuration string
56 // Used to determine if a component has changed.
57 protected final int configHash;
58 protected final String channelConfigurationJson;
59 protected final C channelConfiguration;
61 protected boolean configSeen;
64 * Provide a thingUID and HomeAssistant topic ID to determine the channel group UID and type.
66 * @param thing A ThingUID
67 * @param haID A HomeAssistant topic ID
68 * @param configJson The configuration string
69 * @param gson A Gson instance
71 public AbstractComponent(CFactory.ComponentConfiguration componentConfiguration, Class<C> clazz) {
72 this.componentConfiguration = componentConfiguration;
74 this.channelConfigurationJson = componentConfiguration.getConfigJSON();
75 this.channelConfiguration = componentConfiguration.getConfig(clazz);
76 this.configHash = channelConfigurationJson.hashCode();
78 this.haID = componentConfiguration.getHaID();
80 String groupId = this.haID.getGroupId(channelConfiguration.unique_id);
82 this.channelGroupTypeUID = new ChannelGroupTypeUID(MqttBindingConstants.BINDING_ID, groupId);
83 this.channelGroupUID = new ChannelGroupUID(componentConfiguration.getThingUID(), groupId);
85 this.configSeen = false;
87 String availability_topic = this.channelConfiguration.availability_topic;
88 if (availability_topic != null) {
89 componentConfiguration.getTracker().addAvailabilityTopic(availability_topic,
90 this.channelConfiguration.payload_available, this.channelConfiguration.payload_not_available);
94 protected CChannel.Builder buildChannel(String channelID, Value valueState, String label,
95 ChannelStateUpdateListener channelStateUpdateListener) {
96 return new CChannel.Builder(this, componentConfiguration, channelID, valueState, label,
97 channelStateUpdateListener);
100 public void setConfigSeen() {
101 this.configSeen = true;
105 * Subscribes to all state channels of the component and adds all channels to the provided channel type provider.
107 * @param connection The connection
108 * @param channelStateUpdateListener A listener
109 * @return A future that completes as soon as all subscriptions have been performed. Completes exceptionally on
112 public CompletableFuture<@Nullable Void> start(MqttBrokerConnection connection, ScheduledExecutorService scheduler,
114 return channels.values().parallelStream().map(v -> v.start(connection, scheduler, timeout))
115 .collect(FutureCollector.allOf());
119 * Unsubscribes from all state channels of the component.
121 * @return A future that completes as soon as all subscriptions removals have been performed. Completes
122 * exceptionally on errors.
124 public CompletableFuture<@Nullable Void> stop() {
125 return channels.values().parallelStream().map(CChannel::stop).collect(FutureCollector.allOf());
129 * Add all channel types to the channel type provider.
131 * @param channelTypeProvider The channel type provider
133 public void addChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
134 channelTypeProvider.setChannelGroupType(groupTypeUID(), type());
135 channels.values().forEach(v -> v.addChannelTypes(channelTypeProvider));
139 * Removes all channels from the channel type provider.
140 * Call this if the corresponding Thing handler gets disposed.
142 * @param channelTypeProvider The channel type provider
144 public void removeChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
145 channels.values().forEach(v -> v.removeChannelTypes(channelTypeProvider));
146 channelTypeProvider.removeChannelGroupType(groupTypeUID());
150 * Each HomeAssistant component corresponds to a Channel Group Type.
152 public ChannelGroupTypeUID groupTypeUID() {
153 return channelGroupTypeUID;
157 * The unique id of this component.
159 public ChannelGroupUID uid() {
160 return channelGroupUID;
164 * Component (Channel Group) name.
166 public String name() {
167 return channelConfiguration.name;
171 * Each component consists of multiple Channels.
173 public Map<String, CChannel> channelTypes() {
178 * Return a components channel. A HomeAssistant MQTT component consists of multiple functions
179 * and those are mapped to one or more channels. The channel IDs are constants within the
180 * derived Component, like the {@link ComponentSwitch#switchChannelID}.
182 * @param channelID The channel ID
183 * @return A components channel
185 public @Nullable CChannel channel(String channelID) {
186 return channels.get(channelID);
190 * @return Returns the configuration hash value for easy comparison.
192 public int getConfigHash() {
197 * Return the channel group type.
199 public ChannelGroupType type() {
200 final List<ChannelDefinition> channelDefinitions = channels.values().stream().map(CChannel::type)
201 .collect(Collectors.toList());
202 return ChannelGroupTypeBuilder.instance(channelGroupTypeUID, name()).withChannelDefinitions(channelDefinitions)
207 * Resets all channel states to state UNDEF. Call this method after the connection
208 * to the MQTT broker got lost.
210 public void resetState() {
211 channels.values().forEach(CChannel::resetState);
215 * Return the channel group definition for this component.
217 public ChannelGroupDefinition getGroupDefinition() {
218 return new ChannelGroupDefinition(channelGroupUID.getId(), groupTypeUID(), name(), null);