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