]> git.basschouten.com Git - openhab-addons.git/blob
bd903694abaf4d3ba23b408db096ddcd575c7d0b
[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.digitalstrom.internal.handler;
14
15 import java.util.Arrays;
16 import java.util.HashSet;
17 import java.util.Set;
18
19 import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
20 import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
21 import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
22 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
23 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
24 import org.openhab.core.config.core.Configuration;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.thing.Bridge;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.Thing;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.openhab.core.thing.ThingStatusInfo;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.thing.binding.BaseThingHandler;
34 import org.openhab.core.thing.binding.ThingHandler;
35 import org.openhab.core.types.Command;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * The {@link SceneHandler} is responsible for handling commands, which are sent to the channel of an
41  * DigitalSTROM-Scene.<br>
42  * For that it uses the {@link BridgeHandler} to execute the actual command and implements the
43  * {@link SceneStatusListener} to get informed about changes from the accompanying {@link InternalScene}.
44  *
45  * @author Michael Ochel - Initial contribution
46  * @author Matthias Siegele - Initial contribution
47  *
48  */
49 public class SceneHandler extends BaseThingHandler implements SceneStatusListener {
50
51     private final Logger logger = LoggerFactory.getLogger(SceneHandler.class);
52     /**
53      * Contains all supported thing types of this handler.
54      */
55     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(Arrays.asList(
56             DigitalSTROMBindingConstants.THING_TYPE_APP_SCENE, DigitalSTROMBindingConstants.THING_TYPE_GROUP_SCENE,
57             DigitalSTROMBindingConstants.THING_TYPE_ZONE_SCENE, DigitalSTROMBindingConstants.THING_TYPE_NAMED_SCENE));
58
59     /**
60      * Configured scene does not exist or cannot be used.
61      */
62     public static final String SCENE_WRONG = "sceneWrong";
63     /**
64      * Configured zone does not exist.
65      */
66     public static final String ZONE_WRONG = "zoneWrong";
67     /**
68      * Configured group does not exist.
69      */
70     public static final String GROUP_WRONG = "groupWrong";
71     /**
72      * StructureManager in BridgeHandler is null
73      */
74     public static final String NO_STRUC_MAN = "noStrucMan";
75     /**
76      * Configured scene is null.
77      */
78     public static final String NO_SCENE = "noScene";
79     /**
80      * BridgeHandler is null.
81      */
82     public static final String NO_BRIDGE = "noBridge";
83
84     private BridgeHandler bridgeHandler;
85     private InternalScene scene;
86     private String sceneThingID;
87
88     /**
89      * Creates a new {@link SceneHandler}.
90      *
91      * @param thing must not be null
92      */
93     public SceneHandler(Thing thing) {
94         super(thing);
95     }
96
97     @Override
98     public void initialize() {
99         logger.debug("Initializing SceneHandler");
100         final Bridge bridge = getBridge();
101         if (bridge != null) {
102             bridgeStatusChanged(bridge.getStatusInfo());
103         }
104     }
105
106     @Override
107     public void dispose() {
108         logger.debug("Handler disposed... unregistering SceneStatusListener");
109         if (sceneThingID != null) {
110             BridgeHandler dssBridgeHandler = getBridgeHandler();
111             if (dssBridgeHandler != null) {
112                 getBridgeHandler().unregisterSceneStatusListener(this);
113             }
114             sceneThingID = null;
115             scene = null;
116         }
117     }
118
119     @Override
120     public void handleRemoval() {
121         if (getBridgeHandler() != null) {
122             this.bridgeHandler.childThingRemoved(sceneThingID);
123         }
124         updateStatus(ThingStatus.REMOVED);
125     }
126
127     @Override
128     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
129         if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
130             if (getBridgeHandler() != null) {
131                 if (scene == null) {
132                     String sceneID = getSceneID(getConfig(), bridgeHandler);
133                     switch (sceneID) {
134                         case SCENE_WRONG:
135                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
136                                     "Configured scene '" + getConfig().get(DigitalSTROMBindingConstants.SCENE_ID)
137                                             + "' does not exist or cannot be used, please check the configuration.");
138                             break;
139                         case ZONE_WRONG:
140                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
141                                     "Configured zone '" + getConfig().get(DigitalSTROMBindingConstants.ZONE_ID)
142                                             + "' does not exist, please check the configuration.");
143                             break;
144                         case GROUP_WRONG:
145                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
146                                     "Configured group '" + getConfig().get(DigitalSTROMBindingConstants.GROUP_ID)
147                                             + "' does not exist, please check the configuration.");
148                             break;
149                         case NO_STRUC_MAN:
150                             updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
151                                     "Waiting for building digitalSTROM model.");
152                             break;
153                         case NO_SCENE:
154                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
155                                     "No Scene-ID is set!");
156                             break;
157                         default:
158                             this.sceneThingID = sceneID;
159                             updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
160                                     "Waiting for listener registration");
161                             logger.debug("Set status on {}", getThing().getStatus());
162                             this.bridgeHandler.registerSceneStatusListener(this);
163                     }
164                 } else {
165                     updateStatus(ThingStatus.ONLINE);
166                 }
167             } else {
168                 updateStatus(ThingStatus.OFFLINE);
169             }
170         }
171         if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
172             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
173         }
174         if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
175             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
176         }
177         logger.debug("Set status to {}", getThing().getStatusInfo());
178     }
179
180     /**
181      * Checks the configuration and returns a unique Scene-ID or error string.<br>
182      * The {@link StructureManager} of the {@link BridgeHandler} is used for checking the existing configured zone and
183      * group. The {@link SceneEnum} will be used to check, if the configured scene exists and is allowed to use.<br>
184      * If the check succeed the scene-ID will be returned in format "[zoneID]-[groupID]-[SceneID]", otherwise one of the
185      * following errors {@link String}s will returned:
186      * <ul>
187      * <li>{@link #SCENE_WRONG}: Configured scene does not exist or cannot be used.</li>
188      * <li>{@link #ZONE_WRONG} Configured zone does not exist.</li>
189      * <li>{@link #GROUP_WRONG}: Configured group does not exist.</li>
190      * <li>{@link #NO_STRUC_MAN}: StructureManager in BridgeHandler is null.</li>
191      * <li>{@link #NO_SCENE}: Configured scene is null.</li>
192      * <li>{@link #NO_BRIDGE}: BridgeHandler is null.</li>
193      * </ul>
194      *
195      * @param configuration (must not be null)
196      * @param bridgeHandler (can be null)
197      * @return unique Scene-ID or error string
198      */
199     public static String getSceneID(Configuration configuration, BridgeHandler bridgeHandler) {
200         if (configuration == null) {
201             throw new IllegalArgumentException("configuration cannot be null");
202         }
203         if (bridgeHandler == null) {
204             return NO_BRIDGE;
205         }
206         String configZoneID;
207         String configGroupID;
208         String configSceneID;
209
210         short sceneID;
211         int zoneID;
212         short groupID;
213
214         if (configuration.get(DigitalSTROMBindingConstants.ZONE_ID) != null) {
215             configZoneID = configuration.get(DigitalSTROMBindingConstants.ZONE_ID).toString();
216         } else {
217             configZoneID = "";
218         }
219         if (configuration.get(DigitalSTROMBindingConstants.GROUP_ID) != null) {
220             configGroupID = configuration.get(DigitalSTROMBindingConstants.GROUP_ID).toString();
221         } else {
222             configGroupID = "";
223         }
224         if (configuration.get(DigitalSTROMBindingConstants.SCENE_ID) != null) {
225             configSceneID = configuration.get(DigitalSTROMBindingConstants.SCENE_ID).toString();
226         } else {
227             configSceneID = "";
228         }
229         if (!configSceneID.isEmpty()) {
230             try {
231                 sceneID = Short.parseShort(configSceneID);
232                 if (!SceneEnum.containsScene(sceneID)) {
233                     return SCENE_WRONG;
234                 }
235             } catch (NumberFormatException e) {
236                 try {
237                     sceneID = SceneEnum.valueOf(configSceneID.replace(" ", "_").toUpperCase()).getSceneNumber();
238                 } catch (IllegalArgumentException e1) {
239                     return SCENE_WRONG;
240                 }
241             }
242
243             StructureManager strucMan = bridgeHandler.getStructureManager();
244             if (strucMan != null) {
245                 if (configZoneID.isEmpty()) {
246                     zoneID = 0;
247                 } else {
248                     try {
249                         zoneID = Integer.parseInt(configZoneID);
250                         if (!strucMan.checkZoneID(zoneID)) {
251                             return ZONE_WRONG;
252                         }
253                     } catch (NumberFormatException e) {
254                         zoneID = strucMan.getZoneId(configZoneID);
255                         if (zoneID == -1) {
256                             return ZONE_WRONG;
257                         }
258                     }
259                 }
260
261                 if (configGroupID.isEmpty()) {
262                     groupID = 0;
263                 } else {
264                     try {
265                         groupID = Short.parseShort(configGroupID);
266                         if (!strucMan.checkZoneGroupID(zoneID, groupID)) {
267                             return GROUP_WRONG;
268                         }
269
270                     } catch (NumberFormatException e) {
271                         String zoneName = strucMan.getZoneName(zoneID);
272                         groupID = strucMan.getZoneGroupId(zoneName, configGroupID);
273                     }
274
275                     if (groupID == -1) {
276                         return GROUP_WRONG;
277                     }
278                 }
279                 return zoneID + "-" + groupID + "-" + sceneID;
280             } else {
281                 return NO_STRUC_MAN;
282             }
283         } else {
284             return NO_SCENE;
285         }
286     }
287
288     @Override
289     public void handleCommand(ChannelUID channelUID, Command command) {
290         BridgeHandler dssBridgeHandler = getBridgeHandler();
291         if (dssBridgeHandler == null) {
292             logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
293             return;
294         }
295
296         if (channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
297             if (command instanceof OnOffType) {
298                 if (OnOffType.ON.equals(command)) {
299                     this.bridgeHandler.sendSceneComandToDSS(scene, true);
300                 } else {
301                     this.bridgeHandler.sendSceneComandToDSS(scene, false);
302                 }
303             }
304         } else {
305             logger.warn("Command sent to an unknown channel id: {}", channelUID);
306         }
307     }
308
309     private synchronized BridgeHandler getBridgeHandler() {
310         if (this.bridgeHandler == null) {
311             Bridge bridge = getBridge();
312             if (bridge == null) {
313                 logger.debug("Bridge cannot be found");
314                 return null;
315             }
316             ThingHandler handler = bridge.getHandler();
317
318             if (handler instanceof BridgeHandler) {
319                 this.bridgeHandler = (BridgeHandler) handler;
320             } else {
321                 logger.debug("BridgeHandler cannot be found");
322                 return null;
323             }
324         }
325         return this.bridgeHandler;
326     }
327
328     @Override
329     public void onSceneStateChanged(boolean flag) {
330         if (flag) {
331             updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.ON);
332         } else {
333             updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.OFF);
334         }
335     }
336
337     @Override
338     public void channelLinked(ChannelUID channelUID) {
339         if (scene != null && channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
340             onSceneStateChanged(scene.isActive());
341         }
342     }
343
344     @Override
345     public void onSceneRemoved(InternalScene scene) {
346         this.scene = scene;
347         updateStatus(ThingStatus.OFFLINE);
348         logger.debug("Set status on {}", getThing().getStatus());
349     }
350
351     @Override
352     public void onSceneAdded(InternalScene scene) {
353         logger.debug("Scene {} added", scene.getID());
354         if (this.bridgeHandler != null) {
355             ThingStatusInfo statusInfo = this.bridgeHandler.getThing().getStatusInfo();
356             updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
357             logger.debug("Set status on {}", getThing().getStatus());
358         }
359         this.scene = scene;
360         onSceneStateChanged(scene.isActive());
361     }
362
363     @Override
364     public String getSceneStatusListenerID() {
365         return this.sceneThingID;
366     }
367 }