]> git.basschouten.com Git - openhab-addons.git/blob
d29d4f448bff703b55b13c427d9f200b381afa16
[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 final ExecutorService connectorExecutor = Executors
69             .newSingleThreadExecutor(new NamedThreadFactory(getThing().getUID().getAsString(), true));
70
71     public PilightBridgeHandler(Bridge bridge) {
72         super(bridge);
73     }
74
75     @Override
76     public void handleCommand(ChannelUID channelUID, Command command) {
77         logger.debug("Pilight Bridge is read-only and does not handle commands.");
78     }
79
80     @Override
81     public void initialize() {
82         PilightBridgeConfiguration config = getConfigAs(PilightBridgeConfiguration.class);
83
84         final @Nullable PilightDeviceDiscoveryService discoveryService = this.discoveryService;
85         PilightConnector connector = new PilightConnector(config, new IPilightCallback() {
86             @Override
87             public void updateThingStatus(ThingStatus status, ThingStatusDetail statusDetail,
88                     @Nullable String description) {
89                 updateStatus(status, statusDetail, description);
90                 if (status == ThingStatus.ONLINE) {
91                     refreshConfigAndStatus();
92                 }
93             }
94
95             @Override
96             public void statusReceived(List<Status> allStatus) {
97                 for (Status status : allStatus) {
98                     processStatus(status);
99                 }
100
101                 if (discoveryService != null) {
102                     discoveryService.setStatus(allStatus);
103                 }
104             }
105
106             @Override
107             public void configReceived(Config config) {
108                 processConfig(config);
109             }
110
111             @Override
112             public void versionReceived(Version version) {
113                 getThing().setProperty(Thing.PROPERTY_FIRMWARE_VERSION, version.getVersion());
114             }
115         }, scheduler);
116
117         updateStatus(ThingStatus.UNKNOWN);
118
119         connectorExecutor.execute(connector);
120         this.connector = connector;
121     }
122
123     @Override
124     public void dispose() {
125         final @Nullable ScheduledFuture<?> future = this.refreshJob;
126         if (future != null) {
127             future.cancel(true);
128         }
129
130         final @Nullable PilightConnector connector = this.connector;
131         if (connector != null) {
132             connector.close();
133             this.connector = null;
134         }
135
136         connectorExecutor.shutdown();
137     }
138
139     /**
140      * send action to pilight daemon
141      *
142      * @param action action to send
143      */
144     public void sendAction(Action action) {
145         final @Nullable PilightConnector connector = this.connector;
146         if (connector != null) {
147             connector.sendAction(action);
148         }
149     }
150
151     /**
152      * refresh config and status by requesting config and all values from pilight daemon
153      */
154     public synchronized void refreshConfigAndStatus() {
155         if (thing.getStatus() == ThingStatus.ONLINE) {
156             final @Nullable ScheduledFuture<?> refreshJob = this.refreshJob;
157             if (refreshJob == null || refreshJob.isCancelled() || refreshJob.isDone()) {
158                 logger.debug("schedule refresh of config and status");
159                 this.refreshJob = scheduler.schedule(this::doRefreshConfigAndStatus, REFRESH_CONFIG_MSEC,
160                         TimeUnit.MILLISECONDS);
161             }
162         } else {
163             logger.warn("Bridge is not online - ignoring refresh of config and status.");
164         }
165     }
166
167     private void doRefreshConfigAndStatus() {
168         final @Nullable PilightConnector connector = this.connector;
169         if (connector != null) {
170             // the config is required for dimmers to get the minimum and maximum dim levels
171             connector.refreshConfig();
172             connector.refreshStatus();
173         }
174     }
175
176     /**
177      * Processes a status update received from pilight
178      *
179      * @param status The new Status
180      */
181     private void processStatus(Status status) {
182         final Integer type = status.getType();
183         logger.trace("processStatus device '{}' type {}", status.getDevices().get(0), type);
184
185         if (!DeviceType.SERVER.equals(type)) {
186             for (Thing thing : getThing().getThings()) {
187                 final @Nullable ThingHandler handler = thing.getHandler();
188                 if (handler instanceof PilightBaseHandler baseHandler) {
189                     baseHandler.updateFromStatusIfMatches(status);
190                 }
191             }
192         }
193     }
194
195     @Override
196     public Collection<Class<? extends ThingHandlerService>> getServices() {
197         return Set.of(PilightDeviceDiscoveryService.class);
198     }
199
200     /**
201      * Register discovery service to this bridge instance.
202      */
203     public boolean registerDiscoveryListener(PilightDeviceDiscoveryService listener) {
204         if (discoveryService == null) {
205             discoveryService = listener;
206             return true;
207         }
208         return false;
209     }
210
211     /**
212      * Unregister discovery service from this bridge instance.
213      */
214     public boolean unregisterDiscoveryListener() {
215         if (discoveryService != null) {
216             discoveryService = null;
217             return true;
218         }
219
220         return false;
221     }
222
223     /**
224      * Processes a config received from pilight
225      *
226      * @param config The new config
227      */
228     private void processConfig(Config config) {
229         for (Thing thing : getThing().getThings()) {
230             final @Nullable ThingHandler handler = thing.getHandler();
231             if (handler instanceof PilightBaseHandler baseHandler) {
232                 baseHandler.updateFromConfigIfMatches(config);
233             }
234         }
235
236         final @Nullable PilightDeviceDiscoveryService discoveryService = this.discoveryService;
237         if (discoveryService != null) {
238             discoveryService.setConfig(config);
239         }
240     }
241 }