]> git.basschouten.com Git - openhab-addons.git/blob
e9b53dcd5b95028fa29eb476135fc14320004ff8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.openwebnet.handler;
14
15 import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.*;
16
17 import java.util.Set;
18
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;
39
40 /**
41  * The {@link OpenWebNetLightingHandler} is responsible for handling commands/messages for a Lighting OpenWebNet device.
42  * It extends the abstract {@link OpenWebNetThingHandler}.
43  *
44  * @author Massimo Valla - Initial contribution
45  */
46 @NonNullByDefault
47 public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
48
49     private final Logger logger = LoggerFactory.getLogger(OpenWebNetLightingHandler.class);
50
51     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.LIGHTING_SUPPORTED_THING_TYPES;
52
53     private static final int BRIGHTNESS_CHANGE_DELAY_MSEC = 1500; // delay to wait before sending another brightness
54     // status request
55
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
60
61     public OpenWebNetLightingHandler(Thing thing) {
62         super(thing);
63     }
64
65     @Override
66     protected void requestChannelState(ChannelUID channel) {
67         logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
68         try {
69             send(Lighting.requestStatus(toWhere(channel)));
70         } catch (OWNException e) {
71             logger.warn("Exception while requesting channel {} state: {}", channel, e.getMessage());
72         }
73     }
74
75     @Override
76     protected void handleChannelCommand(ChannelUID channel, Command command) {
77         switch (channel.getId()) {
78             case CHANNEL_BRIGHTNESS:
79                 handleBrightnessCommand(command);
80                 break;
81             case CHANNEL_SWITCH:
82             case CHANNEL_SWITCH_01:
83             case CHANNEL_SWITCH_02:
84                 handleSwitchCommand(channel, command);
85                 break;
86             default: {
87                 logger.warn("Unsupported channel UID {}", channel);
88             }
89         }
90     }
91
92     /**
93      * Handles Lighting switch command for a channel
94      *
95      * @param channel the channel
96      * @param command the command
97      */
98     private void handleSwitchCommand(ChannelUID channel, Command command) {
99         logger.debug("handleSwitchCommand() (command={} - channel={})", command, channel);
100         if (command instanceof OnOffType) {
101             try {
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)));
106                 }
107             } catch (OWNException e) {
108                 logger.warn("Exception while processing command {}: {}", command, e.getMessage());
109             }
110         } else {
111             logger.warn("Unsupported command: {}", command);
112         }
113     }
114
115     /**
116      * Handles Lighting brightness command (ON, OFF, xx%, INCREASE, DECREASE)
117      *
118      * @param command the command
119      */
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);
128             } else { // DECREASE
129                 dimLightTo(latestBrightnessWhat - 1, command);
130             }
131         } else if (command instanceof OnOffType) {
132             if (OnOffType.ON.equals(command)) {
133                 dimLightTo(latestBrightnessWhat, command);
134             } else { // OFF
135                 dimLightTo(0, command);
136             }
137         } else {
138             logger.warn("Cannot handle command {} for thing {}", command, getThing().getUID());
139         }
140     }
141
142     /**
143      * Helper method to dim light to a valid OWN value
144      */
145     private void dimLightTo(int whatInt, Command command) {
146         int newWhatInt = whatInt;
147         logger.debug("-DIM- dimLightTo() latestBriWhat={} latestBriBeforeOff={} briLevelRequested={}",
148                 latestBrightnessWhat, latestBrightnessWhatBeforeOff, brightnessLevelRequested);
149         What newWhat;
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%
155                 newWhatInt = 10;
156             }
157         }
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;
165                 }
166                 // save current brightness level before sending bri=0 command to device
167                 if (newWhatInt == 0) {
168                     latestBrightnessWhatBeforeOff = latestBrightnessWhat;
169                 }
170                 Where w = deviceWhere;
171                 if (w != null) {
172                     try {
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());
177                     }
178                 }
179             } else {
180                 logger.debug("-DIM- do nothing");
181             }
182         } else {
183             logger.debug("-DIM- do nothing");
184         }
185         logger.debug("-DIM- latestBriWhat={} latestBriBeforeOff={} briLevelRequested={}", latestBrightnessWhat,
186                 latestBrightnessWhatBeforeOff, brightnessLevelRequested);
187     }
188
189     @Override
190     protected String ownIdPrefix() {
191         return Who.LIGHTING.value().toString();
192     }
193
194     @Override
195     protected void handleMessage(BaseOpenMessage msg) {
196         super.handleMessage(msg);
197         updateLightState((Lighting) msg);
198     }
199
200     /**
201      * Updates light state based on a OWN Lighting event message received
202      *
203      * @param msg the Lighting message received
204      */
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);
210         } else {
211             updateLightOnOffState(msg);
212         }
213     }
214
215     /**
216      * Updates on/off state based on a OWN Lighting event message received
217      *
218      * @param msg the Lighting message received
219      */
220     private void updateLightOnOffState(Lighting msg) {
221         String channelID;
222         OpenWebNetBridgeHandler brH = bridgeHandler;
223         if (brH != null) {
224             if (brH.isBusGateway()) {
225                 channelID = CHANNEL_SWITCH;
226             } else {
227                 WhereZigBee w = (WhereZigBee) (msg.getWhere());
228                 if (WhereZigBee.UNIT_02.equals(w.getUnit())) {
229                     channelID = CHANNEL_SWITCH_02;
230                 } else {
231                     channelID = CHANNEL_SWITCH_01;
232                 }
233             }
234             if (msg.isOn()) {
235                 updateState(channelID, OnOffType.ON);
236             } else if (msg.isOff()) {
237                 updateState(channelID, OnOffType.OFF);
238             } else {
239                 logger.debug("updateLightOnOffState() Ignoring unsupported WHAT for thing {}. Frame={}",
240                         getThing().getUID(), msg.getFrameValue());
241             }
242         }
243     }
244
245     /**
246      * Updates brightness level based on a OWN Lighting event message received
247      *
248      * @param msg the Lighting message received
249      */
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;
264                 if (w != null) {
265                     try {
266                         send(Lighting.requestStatus(w.value()));
267                         brightnessLevelRequested = true;
268                     } catch (OWNException e) {
269                         logger.warn("  $BRI exception while requesting light state: {}", e.getMessage());
270                     }
271                 }
272             } else {
273                 logger.debug("  $BRI change sent {}<{}ms, NO requestStatus needed", delta,
274                         BRIGHTNESS_CHANGE_DELAY_MSEC);
275             }
276         } else {
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)));
283                     if (msg.isOff()) {
284                         latestBrightnessWhatBeforeOff = latestBrightnessWhat;
285                     }
286                     latestBrightnessWhat = newLevel;
287                 } else {
288                     logger.debug("  $BRI no change");
289                 }
290                 brightnessLevelRequested = false;
291             } else { // dimension notification
292                 if (msg.getDim() == Lighting.DIM.DIMMER_LEVEL_100) {
293                     int newPercent;
294                     try {
295                         newPercent = msg.parseDimmerLevel100();
296                     } catch (FrameException fe) {
297                         logger.warn("updateLightBrightnessState() Wrong value for dimmerLevel100 in message: {}", msg);
298                         return;
299                     }
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;
306                     }
307                     latestBrightnessWhat = newLevel;
308                     brightnessLevelRequested = false;
309                 } else {
310                     logger.warn("updateLightBrightnessState() Cannot handle message {} for thing {}", msg,
311                             getThing().getUID());
312                     return;
313                 }
314             }
315         }
316         logger.debug("  $BRI latestBriWhat={} latestBriBeforeOff={} brightnessLevelRequested={}", latestBrightnessWhat,
317                 latestBrightnessWhatBeforeOff, brightnessLevelRequested);
318     }
319
320     /**
321      * Returns a WHERE address string based on bridge type and unit (optional)
322      *
323      * @param unit the device unit
324      **/
325     @Nullable
326     protected String toWhere(String unit) {
327         Where w = deviceWhere;
328         if (w != null) {
329             OpenWebNetBridgeHandler brH = bridgeHandler;
330             if (brH != null && brH.isBusGateway()) {
331                 return w.value();
332             } else {
333                 return w + unit;
334             }
335         } else {
336             return null;
337         }
338     }
339
340     /**
341      * Returns a WHERE address string based on channel
342      *
343      * @param channel the channel
344      **/
345     @Nullable
346     protected String toWhere(ChannelUID channel) {
347         Where w = deviceWhere;
348         if (w != null) {
349             OpenWebNetBridgeHandler brH = bridgeHandler;
350             if (brH != null) {
351                 if (brH.isBusGateway()) {
352                     return w.value();
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);
357                 }
358             }
359         }
360         return null;
361     }
362 }