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