]> git.basschouten.com Git - openhab-addons.git/blob
1b87feae7794052383347797feb8dc647dfe2eca
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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         logger.debug("Set status to {}", getThing().getStatusInfo());
175     }
176
177     /**
178      * Checks the configuration and returns a unique Scene-ID or error string.<br>
179      * The {@link StructureManager} of the {@link BridgeHandler} is used for checking the existing configured zone and
180      * group. The {@link SceneEnum} will be used to check, if the configured scene exists and is allowed to use.<br>
181      * If the check succeed the scene-ID will be returned in format "[zoneID]-[groupID]-[SceneID]", otherwise one of the
182      * following errors {@link String}s will returned:
183      * <ul>
184      * <li>{@link #SCENE_WRONG}: Configured scene does not exist or cannot be used.</li>
185      * <li>{@link #ZONE_WRONG} Configured zone does not exist.</li>
186      * <li>{@link #GROUP_WRONG}: Configured group does not exist.</li>
187      * <li>{@link #NO_STRUC_MAN}: StructureManager in BridgeHandler is null.</li>
188      * <li>{@link #NO_SCENE}: Configured scene is null.</li>
189      * <li>{@link #NO_BRIDGE}: BridgeHandler is null.</li>
190      * </ul>
191      *
192      * @param configuration (must not be null)
193      * @param bridgeHandler (can be null)
194      * @return unique Scene-ID or error string
195      */
196     public static String getSceneID(Configuration configuration, BridgeHandler bridgeHandler) {
197         if (configuration == null) {
198             throw new IllegalArgumentException("configuration cannot be null");
199         }
200         if (bridgeHandler == null) {
201             return NO_BRIDGE;
202         }
203         String configZoneID;
204         String configGroupID;
205         String configSceneID;
206
207         short sceneID;
208         int zoneID;
209         short groupID;
210
211         if (configuration.get(DigitalSTROMBindingConstants.ZONE_ID) != null) {
212             configZoneID = configuration.get(DigitalSTROMBindingConstants.ZONE_ID).toString();
213         } else {
214             configZoneID = "";
215         }
216         if (configuration.get(DigitalSTROMBindingConstants.GROUP_ID) != null) {
217             configGroupID = configuration.get(DigitalSTROMBindingConstants.GROUP_ID).toString();
218         } else {
219             configGroupID = "";
220         }
221         if (configuration.get(DigitalSTROMBindingConstants.SCENE_ID) != null) {
222             configSceneID = configuration.get(DigitalSTROMBindingConstants.SCENE_ID).toString();
223         } else {
224             configSceneID = "";
225         }
226         if (!configSceneID.isEmpty()) {
227             try {
228                 sceneID = Short.parseShort(configSceneID);
229                 if (!SceneEnum.containsScene(sceneID)) {
230                     return SCENE_WRONG;
231                 }
232             } catch (NumberFormatException e) {
233                 try {
234                     sceneID = SceneEnum.valueOf(configSceneID.replace(" ", "_").toUpperCase()).getSceneNumber();
235                 } catch (IllegalArgumentException e1) {
236                     return SCENE_WRONG;
237                 }
238             }
239
240             StructureManager strucMan = bridgeHandler.getStructureManager();
241             if (strucMan != null) {
242                 if (configZoneID.isEmpty()) {
243                     zoneID = 0;
244                 } else {
245                     try {
246                         zoneID = Integer.parseInt(configZoneID);
247                         if (!strucMan.checkZoneID(zoneID)) {
248                             return ZONE_WRONG;
249                         }
250                     } catch (NumberFormatException e) {
251                         zoneID = strucMan.getZoneId(configZoneID);
252                         if (zoneID == -1) {
253                             return ZONE_WRONG;
254                         }
255                     }
256                 }
257
258                 if (configGroupID.isEmpty()) {
259                     groupID = 0;
260                 } else {
261                     try {
262                         groupID = Short.parseShort(configGroupID);
263                         if (!strucMan.checkZoneGroupID(zoneID, groupID)) {
264                             return GROUP_WRONG;
265                         }
266
267                     } catch (NumberFormatException e) {
268                         String zoneName = strucMan.getZoneName(zoneID);
269                         groupID = strucMan.getZoneGroupId(zoneName, configGroupID);
270                     }
271
272                     if (groupID == -1) {
273                         return GROUP_WRONG;
274                     }
275                 }
276                 return zoneID + "-" + groupID + "-" + sceneID;
277             } else {
278                 return NO_STRUC_MAN;
279             }
280         } else {
281             return NO_SCENE;
282         }
283     }
284
285     @Override
286     public void handleCommand(ChannelUID channelUID, Command command) {
287         BridgeHandler dssBridgeHandler = getBridgeHandler();
288         if (dssBridgeHandler == null) {
289             logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
290             return;
291         }
292
293         if (channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
294             if (command instanceof OnOffType) {
295                 if (OnOffType.ON.equals(command)) {
296                     this.bridgeHandler.sendSceneComandToDSS(scene, true);
297                 } else {
298                     this.bridgeHandler.sendSceneComandToDSS(scene, false);
299                 }
300             }
301         } else {
302             logger.warn("Command sent to an unknown channel id: {}", channelUID);
303         }
304     }
305
306     private synchronized BridgeHandler getBridgeHandler() {
307         if (this.bridgeHandler == null) {
308             Bridge bridge = getBridge();
309             if (bridge == null) {
310                 logger.debug("Bridge cannot be found");
311                 return null;
312             }
313             ThingHandler handler = bridge.getHandler();
314
315             if (handler instanceof BridgeHandler bridgeHandler) {
316                 this.bridgeHandler = bridgeHandler;
317             } else {
318                 logger.debug("BridgeHandler cannot be found");
319                 return null;
320             }
321         }
322         return this.bridgeHandler;
323     }
324
325     @Override
326     public void onSceneStateChanged(boolean flag) {
327         if (flag) {
328             updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.ON);
329         } else {
330             updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.OFF);
331         }
332     }
333
334     @Override
335     public void channelLinked(ChannelUID channelUID) {
336         if (scene != null && channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
337             onSceneStateChanged(scene.isActive());
338         }
339     }
340
341     @Override
342     public void onSceneRemoved(InternalScene scene) {
343         this.scene = scene;
344         updateStatus(ThingStatus.OFFLINE);
345         logger.debug("Set status on {}", getThing().getStatus());
346     }
347
348     @Override
349     public void onSceneAdded(InternalScene scene) {
350         logger.debug("Scene {} added", scene.getID());
351         if (this.bridgeHandler != null) {
352             ThingStatusInfo statusInfo = this.bridgeHandler.getThing().getStatusInfo();
353             updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
354             logger.debug("Set status on {}", getThing().getStatus());
355         }
356         this.scene = scene;
357         onSceneStateChanged(scene.isActive());
358     }
359
360     @Override
361     public String getSceneStatusListenerID() {
362         return this.sceneThingID;
363     }
364 }