2 * Copyright (c) 2010-2020 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.handler;
15 import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.*;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
22 import org.openhab.core.library.types.IncreaseDecreaseType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.thing.Thing;
27 import org.openhab.core.thing.ThingTypeUID;
28 import org.openhab.core.types.Command;
29 import org.openwebnet4j.communication.OWNException;
30 import org.openwebnet4j.message.BaseOpenMessage;
31 import org.openwebnet4j.message.FrameException;
32 import org.openwebnet4j.message.Lighting;
33 import org.openwebnet4j.message.What;
34 import org.openwebnet4j.message.Where;
35 import org.openwebnet4j.message.WhereZigBee;
36 import org.openwebnet4j.message.Who;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link OpenWebNetLightingHandler} is responsible for handling commands/messages for a Lighting OpenWebNet device.
42 * It extends the abstract {@link OpenWebNetThingHandler}.
44 * @author Massimo Valla - Initial contribution
47 public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
49 private final Logger logger = LoggerFactory.getLogger(OpenWebNetLightingHandler.class);
51 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.LIGHTING_SUPPORTED_THING_TYPES;
53 private static final int BRIGHTNESS_CHANGE_DELAY_MSEC = 1500; // delay to wait before sending another brightness
56 private long lastBrightnessChangeSentTS = 0; // timestamp when last brightness change was sent to the device
57 private boolean brightnessLevelRequested = false; // was the brightness level requested ?
58 private int latestBrightnessWhat = -1; // latest brightness WHAT value (-1 = unknown)
59 private int latestBrightnessWhatBeforeOff = -1; // latest brightness WHAT value before device was set to off
61 public OpenWebNetLightingHandler(Thing thing) {
66 protected void requestChannelState(ChannelUID channel) {
67 logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
69 send(Lighting.requestStatus(toWhere(channel)));
70 } catch (OWNException e) {
71 logger.warn("Exception while requesting channel {} state: {}", channel, e.getMessage());
76 protected void handleChannelCommand(ChannelUID channel, Command command) {
77 switch (channel.getId()) {
78 case CHANNEL_BRIGHTNESS:
79 handleBrightnessCommand(command);
82 case CHANNEL_SWITCH_01:
83 case CHANNEL_SWITCH_02:
84 handleSwitchCommand(channel, command);
87 logger.warn("Unsupported channel UID {}", channel);
93 * Handles Lighting switch command for a channel
95 * @param channel the channel
96 * @param command the command
98 private void handleSwitchCommand(ChannelUID channel, Command command) {
99 logger.debug("handleSwitchCommand() (command={} - channel={})", command, channel);
100 if (command instanceof OnOffType) {
102 if (OnOffType.ON.equals(command)) {
103 send(Lighting.requestTurnOn(toWhere(channel)));
104 } else if (OnOffType.OFF.equals(command)) {
105 send(Lighting.requestTurnOff(toWhere(channel)));
107 } catch (OWNException e) {
108 logger.warn("Exception while processing command {}: {}", command, e.getMessage());
111 logger.warn("Unsupported command: {}", command);
116 * Handles Lighting brightness command (ON, OFF, xx%, INCREASE, DECREASE)
118 * @param command the command
120 private void handleBrightnessCommand(Command command) {
121 logger.debug("handleBrightnessCommand() command={}", command);
122 if (command instanceof PercentType) {
123 int percent = ((PercentType) command).intValue();
124 dimLightTo(Lighting.percentToWhat(percent).value(), command);
125 } else if (command instanceof IncreaseDecreaseType) {
126 if (IncreaseDecreaseType.INCREASE.equals(command)) {
127 dimLightTo(latestBrightnessWhat + 1, command);
129 dimLightTo(latestBrightnessWhat - 1, command);
131 } else if (command instanceof OnOffType) {
132 if (OnOffType.ON.equals(command)) {
133 dimLightTo(latestBrightnessWhat, command);
135 dimLightTo(0, command);
138 logger.warn("Cannot handle command {} for thing {}", command, getThing().getUID());
143 * Helper method to dim light to a valid OWN value
145 private void dimLightTo(int whatInt, Command command) {
146 int newWhatInt = whatInt;
147 logger.debug("-DIM- dimLightTo() latestBriWhat={} latestBriBeforeOff={} briLevelRequested={}",
148 latestBrightnessWhat, latestBrightnessWhatBeforeOff, brightnessLevelRequested);
150 if (OnOffType.ON.equals(command) && latestBrightnessWhat <= 0) {
151 // ON after OFF/Unknown -> we reset channel to last value before OFF (if exists)
152 if (latestBrightnessWhatBeforeOff > 0) { // we know last brightness -> set dimmer to it
153 newWhatInt = latestBrightnessWhatBeforeOff;
154 } else { // we do not know last brightness -> set dimmer to 100%
158 logger.debug("-DIM- requested level={}", newWhatInt);
159 if (newWhatInt != latestBrightnessWhat) {
160 if (newWhatInt >= 0 && newWhatInt <= 10) {
161 newWhat = Lighting.WHAT.fromValue(newWhatInt);
162 if (newWhat.equals(Lighting.WHAT.ON)) {
163 // change it to WHAT.DIMMER_20 (dimming to 10% is not allowed in OWN)
164 newWhat = Lighting.WHAT.DIMMER_20;
166 // save current brightness level before sending bri=0 command to device
167 if (newWhatInt == 0) {
168 latestBrightnessWhatBeforeOff = latestBrightnessWhat;
170 Where w = deviceWhere;
173 lastBrightnessChangeSentTS = System.currentTimeMillis();
174 send(Lighting.requestDimTo(w.value(), newWhat));
175 } catch (OWNException e) {
176 logger.warn("Exception while sending dimLightTo for command {}: {}", command, e.getMessage());
180 logger.debug("-DIM- do nothing");
183 logger.debug("-DIM- do nothing");
185 logger.debug("-DIM- latestBriWhat={} latestBriBeforeOff={} briLevelRequested={}", latestBrightnessWhat,
186 latestBrightnessWhatBeforeOff, brightnessLevelRequested);
190 protected String ownIdPrefix() {
191 return Who.LIGHTING.value().toString();
195 protected void handleMessage(BaseOpenMessage msg) {
196 super.handleMessage(msg);
197 updateLightState((Lighting) msg);
201 * Updates light state based on a OWN Lighting event message received
203 * @param msg the Lighting message received
205 private void updateLightState(Lighting msg) {
206 logger.debug("updateLightState() for thing: {}", thing.getUID());
207 ThingTypeUID thingType = thing.getThingTypeUID();
208 if (THING_TYPE_ZB_DIMMER.equals(thingType) || THING_TYPE_BUS_DIMMER.equals(thingType)) {
209 updateLightBrightnessState(msg);
211 updateLightOnOffState(msg);
216 * Updates on/off state based on a OWN Lighting event message received
218 * @param msg the Lighting message received
220 private void updateLightOnOffState(Lighting msg) {
222 OpenWebNetBridgeHandler brH = bridgeHandler;
224 if (brH.isBusGateway()) {
225 channelID = CHANNEL_SWITCH;
227 WhereZigBee w = (WhereZigBee) (msg.getWhere());
228 if (WhereZigBee.UNIT_02.equals(w.getUnit())) {
229 channelID = CHANNEL_SWITCH_02;
231 channelID = CHANNEL_SWITCH_01;
235 updateState(channelID, OnOffType.ON);
236 } else if (msg.isOff()) {
237 updateState(channelID, OnOffType.OFF);
239 logger.debug("updateLightOnOffState() Ignoring unsupported WHAT for thing {}. Frame={}",
240 getThing().getUID(), msg.getFrameValue());
246 * Updates brightness level based on a OWN Lighting event message received
248 * @param msg the Lighting message received
250 private synchronized void updateLightBrightnessState(Lighting msg) {
251 final String channel = CHANNEL_BRIGHTNESS;
252 logger.debug(" $BRI updateLightBrightnessState() msg={}", msg);
253 logger.debug(" $BRI updateLightBr() latestBriWhat={} latestBriBeforeOff={} brightnessLevelRequested={}",
254 latestBrightnessWhat, latestBrightnessWhatBeforeOff, brightnessLevelRequested);
255 long now = System.currentTimeMillis();
256 long delta = now - lastBrightnessChangeSentTS;
257 logger.debug(" $BRI now={} -> delta={}", now, delta);
258 if (msg.isOn() && !brightnessLevelRequested) {
259 if (delta >= BRIGHTNESS_CHANGE_DELAY_MSEC) {
260 // we send a light brightness status request ONLY if last brightness change
261 // was not just sent (>=BRIGHTNESS_CHANGE_DELAY_MSEC ago)
262 logger.debug(" $BRI change sent >={}ms ago, sending requestStatus...", BRIGHTNESS_CHANGE_DELAY_MSEC);
263 Where w = deviceWhere;
266 send(Lighting.requestStatus(w.value()));
267 brightnessLevelRequested = true;
268 } catch (OWNException e) {
269 logger.warn(" $BRI exception while requesting light state: {}", e.getMessage());
273 logger.debug(" $BRI change sent {}<{}ms, NO requestStatus needed", delta,
274 BRIGHTNESS_CHANGE_DELAY_MSEC);
277 logger.debug(" $BRI update from network -> level should be present in WHAT part of the message");
278 if (msg.getWhat() != null) {
279 int newLevel = msg.getWhat().value();
280 logger.debug(" $BRI current level={} ----> new level={}", latestBrightnessWhat, newLevel);
281 if (latestBrightnessWhat != newLevel) {
282 updateState(channel, new PercentType(Lighting.levelToPercent(newLevel)));
284 latestBrightnessWhatBeforeOff = latestBrightnessWhat;
286 latestBrightnessWhat = newLevel;
288 logger.debug(" $BRI no change");
290 brightnessLevelRequested = false;
291 } else { // dimension notification
292 if (msg.getDim() == Lighting.DIM.DIMMER_LEVEL_100) {
295 newPercent = msg.parseDimmerLevel100();
296 } catch (FrameException fe) {
297 logger.warn("updateLightBrightnessState() Wrong value for dimmerLevel100 in message: {}", msg);
300 int newLevel = Lighting.percentToWhat(newPercent).value();
301 logger.debug(" $BRI latest level={} ----> new percent={} ----> new level={}", latestBrightnessWhat,
302 newPercent, newLevel);
303 updateState(channel, new PercentType(newPercent));
304 if (newPercent == 0) {
305 latestBrightnessWhatBeforeOff = latestBrightnessWhat;
307 latestBrightnessWhat = newLevel;
308 brightnessLevelRequested = false;
310 logger.warn("updateLightBrightnessState() Cannot handle message {} for thing {}", msg,
311 getThing().getUID());
316 logger.debug(" $BRI latestBriWhat={} latestBriBeforeOff={} brightnessLevelRequested={}", latestBrightnessWhat,
317 latestBrightnessWhatBeforeOff, brightnessLevelRequested);
321 * Returns a WHERE address string based on bridge type and unit (optional)
323 * @param unit the device unit
326 protected String toWhere(String unit) {
327 Where w = deviceWhere;
329 OpenWebNetBridgeHandler brH = bridgeHandler;
330 if (brH != null && brH.isBusGateway()) {
341 * Returns a WHERE address string based on channel
343 * @param channel the channel
346 protected String toWhere(ChannelUID channel) {
347 Where w = deviceWhere;
349 OpenWebNetBridgeHandler brH = bridgeHandler;
351 if (brH.isBusGateway()) {
353 } else if (channel.getId().equals(CHANNEL_SWITCH_02)) {
354 return ((WhereZigBee) w).valueWithUnit(WhereZigBee.UNIT_02);
355 } else { // CHANNEL_SWITCH_01 or other channels
356 return ((WhereZigBee) w).valueWithUnit(WhereZigBee.UNIT_01);