2 * Copyright (c) 2010-2021 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
13 package org.openhab.binding.openwebnet.internal.handler;
15 import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.*;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
21 import javax.measure.Quantity;
22 import javax.measure.Unit;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.thing.Bridge;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.binding.BaseThingHandler;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.openhab.core.types.State;
36 import org.openhab.core.types.UnDefType;
37 import org.openwebnet4j.OpenGateway;
38 import org.openwebnet4j.communication.OWNException;
39 import org.openwebnet4j.communication.Response;
40 import org.openwebnet4j.message.BaseOpenMessage;
41 import org.openwebnet4j.message.OpenMessage;
42 import org.openwebnet4j.message.Where;
43 import org.openwebnet4j.message.WhereZigBee;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link OpenWebNetThingHandler} is responsible for handling commands for a OpenWebNet device.
49 * It's the abstract class for all OpenWebNet things. It should be extended by each specific OpenWebNet category of
52 * @author Massimo Valla - Initial contribution
55 public abstract class OpenWebNetThingHandler extends BaseThingHandler {
57 private final Logger logger = LoggerFactory.getLogger(OpenWebNetThingHandler.class);
59 protected @Nullable OpenWebNetBridgeHandler bridgeHandler;
60 protected @Nullable String ownId; // OpenWebNet identifier for this device: WHO.WHERE
61 protected @Nullable Where deviceWhere; // this device Where address
63 protected @Nullable ScheduledFuture<?> refreshTimeout;
65 public OpenWebNetThingHandler(Thing thing) {
70 public void initialize() {
71 Bridge bridge = getBridge();
73 OpenWebNetBridgeHandler brH = (OpenWebNetBridgeHandler) bridge.getHandler();
77 final String configDeviceWhere = (String) getConfig().get(CONFIG_PROPERTY_WHERE);
78 if (configDeviceWhere == null) {
79 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
80 "@text/offline.conf-error-where");
84 if (brH.isBusGateway()) {
85 w = buildBusWhere(configDeviceWhere);
87 w = new WhereZigBee(configDeviceWhere);
89 } catch (IllegalArgumentException ia) {
90 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
91 "@text/offline.conf-error-where");
95 final String oid = brH.ownIdFromDeviceWhere(w, this);
97 Map<String, String> properties = editProperties();
98 properties.put(PROPERTY_OWNID, oid);
99 updateProperties(properties);
100 brH.registerDevice(oid, this);
101 logger.debug("associated thing to bridge with ownId={}", ownId);
102 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/unknown.waiting-state");
106 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
107 "@text/offline.conf-error-no-bridge");
112 public void handleCommand(ChannelUID channel, Command command) {
113 logger.debug("handleCommand() (command={} - channel={})", command, channel);
114 OpenWebNetBridgeHandler handler = bridgeHandler;
115 if (handler != null) {
116 OpenGateway gw = handler.gateway;
117 if (gw != null && !gw.isConnected()) {
118 logger.info("Cannot handle {} command for {}: gateway is not connected", command, getThing().getUID());
121 if (deviceWhere == null) {
122 logger.info("Cannot handle {} command for {}: 'where' parameter is not configured or is invalid",
123 command, getThing().getUID());
126 if (command instanceof RefreshType) {
127 requestChannelState(channel);
128 // set a schedule to put device OFFLINE if no answer is received after THING_STATE_REQ_TIMEOUT_SEC
129 refreshTimeout = scheduler.schedule(() -> {
130 if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
131 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
132 "Could not get channel state (timer expired)");
134 }, THING_STATE_REQ_TIMEOUT_SEC, TimeUnit.SECONDS);
136 handleChannelCommand(channel, command);
139 logger.debug("Thing {} is not associated to any gateway, skipping command", getThing().getUID());
144 * Handles a command for the specific channel for this thing.
145 * It must be implemented by each specific OpenWebNet category of device (WHO), based on channel
147 * @param channel specific ChannleUID
148 * @param command the Command to be executed
150 protected abstract void handleChannelCommand(ChannelUID channel, Command command);
153 * Handle incoming message from OWN network via bridge Thing, directed to this device. It should be further
154 * implemented by each specific device handler.
156 * @param msg the message to handle
158 protected void handleMessage(BaseOpenMessage msg) {
159 ThingStatus ts = getThing().getStatus();
160 if (ThingStatus.ONLINE != ts && ThingStatus.REMOVING != ts && ThingStatus.REMOVED != ts) {
161 updateStatus(ThingStatus.ONLINE);
166 * Helper method to send OWN messages from ThingHandlers
168 public @Nullable Response send(OpenMessage msg) throws OWNException {
169 OpenWebNetBridgeHandler bh = bridgeHandler;
171 OpenGateway gw = bh.gateway;
176 logger.warn("Couldn't send message {}: handler or gateway is null", msg);
181 * Helper method to send with high priority OWN messages from ThingsHandlers
183 protected @Nullable Response sendHighPriority(OpenMessage msg) throws OWNException {
184 OpenWebNetBridgeHandler handler = bridgeHandler;
185 if (handler != null) {
186 OpenGateway gw = handler.gateway;
188 return gw.sendHighPriority(msg);
195 * Request the state for the specified channel
197 * @param channel the {@link ChannelUID} to request the state for
199 protected abstract void requestChannelState(ChannelUID channel);
204 * @param refreshAll set true if all devices of the binding should be refreshed with one command, if possible
206 protected abstract void refreshDevice(boolean refreshAll);
209 * Abstract builder for device Where address, to be implemented by each subclass to choose the right Where subclass
210 * (the method is used only if the Thing is associated to a BUS gateway).
212 * @param wStr the WHERE string
214 protected abstract Where buildBusWhere(String wStr) throws IllegalArgumentException;
217 public void dispose() {
218 OpenWebNetBridgeHandler bh = bridgeHandler;
220 if (bh != null && oid != null) {
221 bh.unregisterDevice(oid);
223 ScheduledFuture<?> sc = refreshTimeout;
231 * Helper method to return a Quantity from a Number value or UnDefType.NULL if value is null
233 * @param value to be used
234 * @param unit to be used
237 protected <U extends Quantity<U>> State getAsQuantityTypeOrNull(@Nullable Number value, Unit<U> unit) {
238 return value == null ? UnDefType.NULL : new QuantityType<>(value, unit);
242 * Returns a prefix String for ownId specific for each handler. To be implemented by sub-classes.
246 protected abstract String ownIdPrefix();