]> git.basschouten.com Git - openhab-addons.git/blob
dfcfd9fdfd08782e551c18d3110be3f4b9cdebe8
[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
14 package org.openhab.binding.pilight.internal.handler;
15
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.concurrent.ExecutorService;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.pilight.internal.IPilightCallback;
27 import org.openhab.binding.pilight.internal.PilightBridgeConfiguration;
28 import org.openhab.binding.pilight.internal.PilightConnector;
29 import org.openhab.binding.pilight.internal.discovery.PilightDeviceDiscoveryService;
30 import org.openhab.binding.pilight.internal.dto.*;
31 import org.openhab.core.common.NamedThreadFactory;
32 import org.openhab.core.thing.*;
33 import org.openhab.core.thing.binding.BaseBridgeHandler;
34 import org.openhab.core.thing.binding.ThingHandler;
35 import org.openhab.core.thing.binding.ThingHandlerService;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link PilightBridgeHandler} is responsible dispatching commands for the child
42  * things to the Pilight daemon and sending status updates to the child things.
43  *
44  * @author Stefan Röllin - Initial contribution
45  * @author Niklas Dörfler - Port pilight binding to openHAB 3 + add device discovery
46  */
47 @NonNullByDefault
48 public class PilightBridgeHandler extends BaseBridgeHandler {
49
50     private static final int REFRESH_CONFIG_MSEC = 500;
51
52     private final Logger logger = LoggerFactory.getLogger(PilightBridgeHandler.class);
53
54     private @Nullable PilightConnector connector = null;
55
56     private @Nullable ScheduledFuture<?> refreshJob = null;
57
58     private @Nullable PilightDeviceDiscoveryService discoveryService = null;
59
60     private final ExecutorService connectorExecutor = Executors
61             .newSingleThreadExecutor(new NamedThreadFactory(getThing().getUID().getAsString(), true));
62
63     public PilightBridgeHandler(Bridge bridge) {
64         super(bridge);
65     }
66
67     @Override
68     public void handleCommand(ChannelUID channelUID, Command command) {
69         logger.debug("Pilight Bridge is read-only and does not handle commands.");
70     }
71
72     @Override
73     public void initialize() {
74         PilightBridgeConfiguration config = getConfigAs(PilightBridgeConfiguration.class);
75
76         final @Nullable PilightDeviceDiscoveryService discoveryService = this.discoveryService;
77         PilightConnector connector = new PilightConnector(config, new IPilightCallback() {
78             @Override
79             public void updateThingStatus(ThingStatus status, ThingStatusDetail statusDetail,
80                     @Nullable String description) {
81                 updateStatus(status, statusDetail, description);
82                 if (status == ThingStatus.ONLINE) {
83                     refreshConfigAndStatus();
84                 }
85             }
86
87             @Override
88             public void statusReceived(List<Status> allStatus) {
89                 for (Status status : allStatus) {
90                     processStatus(status);
91                 }
92
93                 if (discoveryService != null) {
94                     discoveryService.setStatus(allStatus);
95                 }
96             }
97
98             @Override
99             public void configReceived(Config config) {
100                 processConfig(config);
101             }
102
103             @Override
104             public void versionReceived(Version version) {
105                 getThing().setProperty(Thing.PROPERTY_FIRMWARE_VERSION, version.getVersion());
106             }
107         }, scheduler);
108
109         updateStatus(ThingStatus.UNKNOWN);
110
111         connectorExecutor.execute(connector);
112         this.connector = connector;
113     }
114
115     @Override
116     public void dispose() {
117         final @Nullable ScheduledFuture<?> future = this.refreshJob;
118         if (future != null) {
119             future.cancel(true);
120         }
121
122         final @Nullable PilightConnector connector = this.connector;
123         if (connector != null) {
124             connector.close();
125             this.connector = null;
126         }
127
128         connectorExecutor.shutdown();
129     }
130
131     /**
132      * send action to pilight daemon
133      *
134      * @param action action to send
135      */
136     public void sendAction(Action action) {
137         final @Nullable PilightConnector connector = this.connector;
138         if (connector != null) {
139             connector.sendAction(action);
140         }
141     }
142
143     /**
144      * refresh config and status by requesting config and all values from pilight daemon
145      */
146     public synchronized void refreshConfigAndStatus() {
147         if (thing.getStatus() == ThingStatus.ONLINE) {
148             final @Nullable ScheduledFuture<?> refreshJob = this.refreshJob;
149             if (refreshJob == null || refreshJob.isCancelled() || refreshJob.isDone()) {
150                 logger.debug("schedule refresh of config and status");
151                 this.refreshJob = scheduler.schedule(this::doRefreshConfigAndStatus, REFRESH_CONFIG_MSEC,
152                         TimeUnit.MILLISECONDS);
153             }
154         } else {
155             logger.warn("Bridge is not online - ignoring refresh of config and status.");
156         }
157     }
158
159     private void doRefreshConfigAndStatus() {
160         final @Nullable PilightConnector connector = this.connector;
161         if (connector != null) {
162             // the config is required for dimmers to get the minimum and maximum dim levels
163             connector.refreshConfig();
164             connector.refreshStatus();
165         }
166     }
167
168     /**
169      * Processes a status update received from pilight
170      *
171      * @param status The new Status
172      */
173     private void processStatus(Status status) {
174         final Integer type = status.getType();
175         logger.trace("processStatus device '{}' type {}", status.getDevices().get(0), type);
176
177         if (!DeviceType.SERVER.equals(type)) {
178             for (Thing thing : getThing().getThings()) {
179                 final @Nullable ThingHandler handler = thing.getHandler();
180                 if (handler instanceof PilightBaseHandler) {
181                     ((PilightBaseHandler) handler).updateFromStatusIfMatches(status);
182                 }
183             }
184         }
185     }
186
187     @Override
188     public Collection<Class<? extends ThingHandlerService>> getServices() {
189         return Collections.singleton(PilightDeviceDiscoveryService.class);
190     }
191
192     /**
193      * Register discovery service to this bridge instance.
194      */
195     public boolean registerDiscoveryListener(PilightDeviceDiscoveryService listener) {
196         if (discoveryService == null) {
197             discoveryService = listener;
198             return true;
199         }
200         return false;
201     }
202
203     /**
204      * Unregister discovery service from this bridge instance.
205      */
206     public boolean unregisterDiscoveryListener() {
207         if (discoveryService != null) {
208             discoveryService = null;
209             return true;
210         }
211
212         return false;
213     }
214
215     /**
216      * Processes a config received from pilight
217      *
218      * @param config The new config
219      */
220     private void processConfig(Config config) {
221         for (Thing thing : getThing().getThings()) {
222             final @Nullable ThingHandler handler = thing.getHandler();
223             if (handler instanceof PilightBaseHandler) {
224                 ((PilightBaseHandler) handler).updateFromConfigIfMatches(config);
225             }
226         }
227
228         final @Nullable PilightDeviceDiscoveryService discoveryService = this.discoveryService;
229         if (discoveryService != null) {
230             discoveryService.setConfig(config);
231         }
232     }
233 }