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
14 package org.openhab.binding.pilight.internal.handler;
16 import java.util.Collection;
17 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;
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;
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.
52 * @author Stefan Röllin - Initial contribution
53 * @author Niklas Dörfler - Port pilight binding to openHAB 3 + add device discovery
56 public class PilightBridgeHandler extends BaseBridgeHandler {
58 private static final int REFRESH_CONFIG_MSEC = 500;
60 private final Logger logger = LoggerFactory.getLogger(PilightBridgeHandler.class);
62 private @Nullable PilightConnector connector = null;
64 private @Nullable ScheduledFuture<?> refreshJob = null;
66 private @Nullable PilightDeviceDiscoveryService discoveryService = null;
68 private final ExecutorService connectorExecutor = Executors
69 .newSingleThreadExecutor(new NamedThreadFactory(getThing().getUID().getAsString(), true));
71 public PilightBridgeHandler(Bridge bridge) {
76 public void handleCommand(ChannelUID channelUID, Command command) {
77 logger.debug("Pilight Bridge is read-only and does not handle commands.");
81 public void initialize() {
82 PilightBridgeConfiguration config = getConfigAs(PilightBridgeConfiguration.class);
84 final @Nullable PilightDeviceDiscoveryService discoveryService = this.discoveryService;
85 PilightConnector connector = new PilightConnector(config, new IPilightCallback() {
87 public void updateThingStatus(ThingStatus status, ThingStatusDetail statusDetail,
88 @Nullable String description) {
89 updateStatus(status, statusDetail, description);
90 if (status == ThingStatus.ONLINE) {
91 refreshConfigAndStatus();
96 public void statusReceived(List<Status> allStatus) {
97 for (Status status : allStatus) {
98 processStatus(status);
101 if (discoveryService != null) {
102 discoveryService.setStatus(allStatus);
107 public void configReceived(Config config) {
108 processConfig(config);
112 public void versionReceived(Version version) {
113 getThing().setProperty(Thing.PROPERTY_FIRMWARE_VERSION, version.getVersion());
117 updateStatus(ThingStatus.UNKNOWN);
119 connectorExecutor.execute(connector);
120 this.connector = connector;
124 public void dispose() {
125 final @Nullable ScheduledFuture<?> future = this.refreshJob;
126 if (future != null) {
130 final @Nullable PilightConnector connector = this.connector;
131 if (connector != null) {
133 this.connector = null;
136 connectorExecutor.shutdown();
140 * send action to pilight daemon
142 * @param action action to send
144 public void sendAction(Action action) {
145 final @Nullable PilightConnector connector = this.connector;
146 if (connector != null) {
147 connector.sendAction(action);
152 * refresh config and status by requesting config and all values from pilight daemon
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);
163 logger.warn("Bridge is not online - ignoring refresh of config and status.");
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();
177 * Processes a status update received from pilight
179 * @param status The new Status
181 private void processStatus(Status status) {
182 final Integer type = status.getType();
183 logger.trace("processStatus device '{}' type {}", status.getDevices().get(0), type);
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);
196 public Collection<Class<? extends ThingHandlerService>> getServices() {
197 return Set.of(PilightDeviceDiscoveryService.class);
201 * Register discovery service to this bridge instance.
203 public boolean registerDiscoveryListener(PilightDeviceDiscoveryService listener) {
204 if (discoveryService == null) {
205 discoveryService = listener;
212 * Unregister discovery service from this bridge instance.
214 public boolean unregisterDiscoveryListener() {
215 if (discoveryService != null) {
216 discoveryService = null;
224 * Processes a config received from pilight
226 * @param config The new config
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);
236 final @Nullable PilightDeviceDiscoveryService discoveryService = this.discoveryService;
237 if (discoveryService != null) {
238 discoveryService.setConfig(config);