2 * Copyright (c) 2010-2023 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.remoteopenhab.internal.handler;
15 import static org.openhab.binding.remoteopenhab.internal.RemoteopenhabBindingConstants.*;
16 import static org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabTriggerChannelConfiguration.CHANNEL_UID;
18 import java.util.ArrayList;
19 import java.util.List;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabThingConfiguration;
24 import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabChannel;
25 import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStatusInfo;
26 import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabThing;
27 import org.openhab.binding.remoteopenhab.internal.exceptions.RemoteopenhabException;
28 import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabThingsDataListener;
29 import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
30 import org.openhab.core.config.core.Configuration;
31 import org.openhab.core.thing.Bridge;
32 import org.openhab.core.thing.Channel;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.thing.ThingStatusInfo;
38 import org.openhab.core.thing.binding.BaseThingHandler;
39 import org.openhab.core.thing.binding.BridgeHandler;
40 import org.openhab.core.thing.binding.builder.ChannelBuilder;
41 import org.openhab.core.thing.binding.builder.ThingBuilder;
42 import org.openhab.core.thing.type.ChannelKind;
43 import org.openhab.core.thing.type.ChannelTypeUID;
44 import org.openhab.core.types.Command;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * The {@link RemoteopenhabThingHandler} is responsible for handling status updates associated to
52 * @author Laurent Garnier - Initial contribution
55 public class RemoteopenhabThingHandler extends BaseThingHandler implements RemoteopenhabThingsDataListener {
57 private final Logger logger = LoggerFactory.getLogger(RemoteopenhabThingHandler.class);
59 private @Nullable RemoteopenhabRestClient restClient;
61 private @NonNullByDefault({}) RemoteopenhabThingConfiguration config;
63 public RemoteopenhabThingHandler(Thing thing) {
68 public void handleCommand(ChannelUID channelUID, Command command) {
69 // No state channel defined for this thing type and so no command to handle
73 public void initialize() {
74 logger.debug("initializing remote openHAB handler for thing {}", getThing().getUID());
75 Bridge bridge = getBridge();
76 initializeThing(bridge != null ? bridge.getStatus() : null);
80 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
81 logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
82 initializeThing(bridgeStatusInfo.getStatus());
85 private void initializeThing(@Nullable ThingStatus bridgeStatus) {
86 Bridge bridge = getBridge();
87 BridgeHandler bridgeHandler = bridge != null ? bridge.getHandler() : null;
88 RemoteopenhabRestClient oldClient = this.restClient;
89 if (oldClient != null) {
90 oldClient.removeThingsDataListener(this);
91 this.restClient = null;
93 if (bridgeHandler != null && bridgeStatus != null) {
94 if (bridgeStatus == ThingStatus.ONLINE) {
95 config = getConfigAs(RemoteopenhabThingConfiguration.class);
97 String uid = getConfigThingUID();
98 if (uid.length() == 0) {
99 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
100 "@text/offline.config-error-undefined-thing-uid");
102 RemoteopenhabRestClient client = ((RemoteopenhabBridgeHandler) bridgeHandler).gestRestClient();
103 client.addThingsDataListener(this);
104 this.restClient = client;
106 updateStatus(ThingStatus.UNKNOWN);
108 scheduler.execute(() -> {
110 RemoteopenhabThing thing = client.getRemoteThing(uid);
111 createTriggerChannels(thing, config.buildTriggerChannels);
112 RemoteopenhabStatusInfo statusInfo = thing.statusInfo;
113 if (statusInfo != null) {
114 updateThingStatus(uid, statusInfo);
116 } catch (RemoteopenhabException e) {
117 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getRawMessage());
122 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
125 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
130 public void dispose() {
131 logger.debug("Disposing remote openHAB handler for thing {}", getThing().getUID());
132 RemoteopenhabRestClient client = this.restClient;
133 if (client != null) {
134 client.removeThingsDataListener(this);
135 this.restClient = null;
140 private String getConfigThingUID() {
141 return config.thingUID.trim();
144 private void createTriggerChannels(RemoteopenhabThing thing, boolean addNewChannels) {
145 List<Channel> channels = new ArrayList<>();
146 for (RemoteopenhabChannel channelDTO : thing.channels) {
147 if (!"TRIGGER".equals(channelDTO.kind)) {
150 ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TYPE_TRIGGER);
151 ChannelUID channelUID = new ChannelUID(getThing().getUID(),
152 channelDTO.uid.replaceAll("[^A-Za-z0-9_]", "_"));
153 Configuration channelConfig = new Configuration();
154 channelConfig.put(CHANNEL_UID, channelDTO.uid);
155 logger.trace("Create the channel {} of type {}", channelUID, channelTypeUID);
156 channels.add(ChannelBuilder.create(channelUID, null).withType(channelTypeUID).withKind(ChannelKind.TRIGGER)
157 .withLabel(channelDTO.label).withDescription(channelDTO.description)
158 .withConfiguration(channelConfig).build());
160 if (!channels.isEmpty()) {
161 ThingBuilder thingBuilder = editThing();
163 for (Channel channel : channels) {
164 if (getThing().getChannel(channel.getUID()) != null) {
165 thingBuilder.withoutChannel(channel.getUID());
170 logger.debug("{} trigger channels removed for the thing {}", nbRemoved, getThing().getUID());
173 if (addNewChannels) {
174 for (Channel channel : channels) {
175 thingBuilder.withChannel(channel);
177 nbAdded = channels.size();
178 logger.debug("{} trigger channels added for the thing {}", nbAdded, getThing().getUID());
180 if (nbRemoved > 0 || nbAdded > 0) {
181 updateThing(thingBuilder.build());
187 public void onThingStatusUpdated(String thingUID, RemoteopenhabStatusInfo statusInfo) {
188 if (thingUID.equals(getConfigThingUID())) {
189 updateThingStatus(thingUID, statusInfo);
194 public void onThingAdded(RemoteopenhabThing thing) {
199 public void onThingRemoved(RemoteopenhabThing thing) {
204 public void onChannelTriggered(String channelUID, @Nullable String event) {
205 String thingUID = channelUID.substring(0, channelUID.lastIndexOf(":"));
206 if (thingUID.equals(getConfigThingUID())) {
207 for (Channel channel : getThing().getChannels()) {
208 if (channel.getKind() == ChannelKind.TRIGGER
209 && channelUID.equals(channel.getConfiguration().get(CHANNEL_UID))) {
211 triggerChannel(channel.getUID());
212 logger.debug("triggerChannel {}", channel.getUID());
214 triggerChannel(channel.getUID(), event);
215 logger.debug("triggerChannel {} with event {}", channel.getUID(), event);
222 private void updateThingStatus(String thingUID, RemoteopenhabStatusInfo statusInfo) {
223 ThingStatus status = ThingStatus.valueOf(statusInfo.status);
224 // All remote status different from UNKNOWN or ONLINE or OFFLINE is considered as OFFLINE
225 if (status != ThingStatus.UNKNOWN && status != ThingStatus.ONLINE && status != ThingStatus.OFFLINE) {
226 status = ThingStatus.OFFLINE;
228 ThingStatusDetail detail = ThingStatusDetail.valueOf(statusInfo.statusDetail);
229 updateStatus(status, detail, statusInfo.description);
230 logger.debug("updateStatus {} with status {} detail {} description {}", thingUID, status, detail,
231 statusInfo.description);