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