]> git.basschouten.com Git - openhab-addons.git/blob
67a78909b7b9e68501f886843294a18fb7cd98c9
[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.net.URI;
16 import java.util.concurrent.CompletableFuture;
17 import java.util.concurrent.ScheduledExecutorService;
18
19 import org.apache.commons.lang.StringUtils;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.mqtt.generic.ChannelConfigBuilder;
23 import org.openhab.binding.mqtt.generic.ChannelState;
24 import org.openhab.binding.mqtt.generic.ChannelStateTransformation;
25 import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
26 import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
27 import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
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.CFactory.ComponentConfiguration;
31 import org.openhab.core.config.core.Configuration;
32 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
33 import org.openhab.core.thing.Channel;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.binding.builder.ChannelBuilder;
36 import org.openhab.core.thing.type.ChannelDefinition;
37 import org.openhab.core.thing.type.ChannelDefinitionBuilder;
38 import org.openhab.core.thing.type.ChannelType;
39 import org.openhab.core.thing.type.ChannelTypeBuilder;
40 import org.openhab.core.thing.type.ChannelTypeUID;
41 import org.openhab.core.types.StateDescription;
42
43 /**
44  * An {@link AbstractComponent}s derived class consists of one or multiple channels.
45  * Each component channel consists of the determined channel type, channel type UID and the
46  * channel description itself as well as the the channels state.
47  *
48  * After the discovery process has completed and the tree of components and component channels
49  * have been built up, the channel types are registered to a custom channel type provider
50  * before adding the channel descriptions to the Thing themselves.
51  * <br>
52  * <br>
53  * An object of this class creates the required {@link ChannelType} and {@link ChannelTypeUID} as well
54  * as keeps the {@link ChannelState} and {@link Channel} in one place.
55  *
56  * @author David Graeff - Initial contribution
57  */
58 @NonNullByDefault
59 public class CChannel {
60     private static final String JINJA = "JINJA";
61
62     private final ChannelUID channelUID;
63     private final ChannelState channelState;
64     private final Channel channel;
65     private final ChannelType type;
66     private final ChannelTypeUID channelTypeUID;
67     private final ChannelStateUpdateListener channelStateUpdateListener;
68
69     private CChannel(ChannelUID channelUID, ChannelState channelState, Channel channel, ChannelType type,
70             ChannelTypeUID channelTypeUID, ChannelStateUpdateListener channelStateUpdateListener) {
71         super();
72         this.channelUID = channelUID;
73         this.channelState = channelState;
74         this.channel = channel;
75         this.type = type;
76         this.channelTypeUID = channelTypeUID;
77         this.channelStateUpdateListener = channelStateUpdateListener;
78     }
79
80     public ChannelUID getChannelUID() {
81         return channelUID;
82     }
83
84     public Channel getChannel() {
85         return channel;
86     }
87
88     public ChannelState getState() {
89         return channelState;
90     }
91
92     public CompletableFuture<@Nullable Void> stop() {
93         return channelState.stop();
94     }
95
96     public CompletableFuture<@Nullable Void> start(MqttBrokerConnection connection, ScheduledExecutorService scheduler,
97             int timeout) {
98         // Make sure we set the callback again which might have been nulled during an stop
99         channelState.setChannelStateUpdateListener(this.channelStateUpdateListener);
100
101         return channelState.start(connection, scheduler, timeout);
102     }
103
104     public void addChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
105         channelTypeProvider.setChannelType(channelTypeUID, type);
106     }
107
108     public void removeChannelTypes(MqttChannelTypeProvider channelTypeProvider) {
109         channelTypeProvider.removeChannelType(channelTypeUID);
110     }
111
112     public ChannelDefinition type() {
113         return new ChannelDefinitionBuilder(channelUID.getId(), channelTypeUID).build();
114     }
115
116     public void resetState() {
117         channelState.getCache().resetState();
118     }
119
120     public static class Builder {
121         private AbstractComponent<?> component;
122         private ComponentConfiguration componentConfiguration;
123         private String channelID;
124         private Value valueState;
125         private String label;
126         private @Nullable String state_topic;
127         private @Nullable String command_topic;
128         private boolean retain;
129         private boolean trigger;
130         private @Nullable Integer qos;
131         private ChannelStateUpdateListener channelStateUpdateListener;
132
133         private @Nullable String templateIn;
134
135         public Builder(AbstractComponent<?> component, ComponentConfiguration componentConfiguration, String channelID,
136                 Value valueState, String label, ChannelStateUpdateListener channelStateUpdateListener) {
137             this.component = component;
138             this.componentConfiguration = componentConfiguration;
139             this.channelID = channelID;
140             this.valueState = valueState;
141             this.label = label;
142             this.channelStateUpdateListener = channelStateUpdateListener;
143         }
144
145         public Builder stateTopic(@Nullable String state_topic) {
146             this.state_topic = state_topic;
147             return this;
148         }
149
150         public Builder stateTopic(@Nullable String state_topic, @Nullable String... templates) {
151             this.state_topic = state_topic;
152             if (StringUtils.isNotBlank(state_topic)) {
153                 for (String template : templates) {
154                     if (StringUtils.isNotBlank(template)) {
155                         this.templateIn = template;
156                         break;
157                     }
158                 }
159             }
160             return this;
161         }
162
163         /**
164          * @deprecated use commandTopic(String, boolean, int)
165          * @param command_topic
166          * @param retain
167          * @return
168          */
169         @Deprecated
170         public Builder commandTopic(@Nullable String command_topic, boolean retain) {
171             this.command_topic = command_topic;
172             this.retain = retain;
173             return this;
174         }
175
176         public Builder commandTopic(@Nullable String command_topic, boolean retain, int qos) {
177             this.command_topic = command_topic;
178             this.retain = retain;
179             this.qos = qos;
180             return this;
181         }
182
183         public Builder trigger(boolean trigger) {
184             this.trigger = trigger;
185             return this;
186         }
187
188         public CChannel build() {
189             return build(true);
190         }
191
192         public CChannel build(boolean addToComponent) {
193             ChannelUID channelUID;
194             ChannelState channelState;
195             Channel channel;
196             ChannelType type;
197             ChannelTypeUID channelTypeUID;
198
199             channelUID = new ChannelUID(component.channelGroupUID, channelID);
200             channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID,
201                     channelUID.getGroupId() + "_" + channelID);
202             channelState = new ChannelState(
203                     ChannelConfigBuilder.create().withRetain(retain).withQos(qos).withStateTopic(state_topic)
204                             .withCommandTopic(command_topic).makeTrigger(trigger).build(),
205                     channelUID, valueState, channelStateUpdateListener);
206
207             if (StringUtils.isBlank(state_topic) || this.trigger) {
208                 type = ChannelTypeBuilder.trigger(channelTypeUID, label)
209                         .withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HA_CHANNEL)).build();
210             } else {
211                 StateDescription description = valueState.createStateDescription(command_topic == null).build()
212                         .toStateDescription();
213                 type = ChannelTypeBuilder.state(channelTypeUID, label, channelState.getItemType())
214                         .withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HA_CHANNEL))
215                         .withStateDescription(description).build();
216             }
217
218             Configuration configuration = new Configuration();
219             configuration.put("config", component.channelConfigurationJson);
220             component.haID.toConfig(configuration);
221
222             channel = ChannelBuilder.create(channelUID, channelState.getItemType()).withType(channelTypeUID)
223                     .withKind(type.getKind()).withLabel(label).withConfiguration(configuration).build();
224
225             CChannel result = new CChannel(channelUID, channelState, channel, type, channelTypeUID,
226                     channelStateUpdateListener);
227
228             @Nullable
229             TransformationServiceProvider transformationProvider = componentConfiguration
230                     .getTransformationServiceProvider();
231
232             final String templateIn = this.templateIn;
233             if (templateIn != null && transformationProvider != null) {
234                 channelState
235                         .addTransformation(new ChannelStateTransformation(JINJA, templateIn, transformationProvider));
236             }
237             if (addToComponent) {
238                 component.channels.put(channelID, result);
239             }
240             return result;
241         }
242     }
243 }