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