2 * Copyright (c) 2010-2023 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.digitalstrom.internal.handler;
15 import java.util.Arrays;
16 import java.util.HashSet;
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;
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}.
45 * @author Michael Ochel - Initial contribution
46 * @author Matthias Siegele - Initial contribution
49 public class SceneHandler extends BaseThingHandler implements SceneStatusListener {
51 private final Logger logger = LoggerFactory.getLogger(SceneHandler.class);
53 * Contains all supported thing types of this handler.
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));
60 * Configured scene does not exist or cannot be used.
62 public static final String SCENE_WRONG = "sceneWrong";
64 * Configured zone does not exist.
66 public static final String ZONE_WRONG = "zoneWrong";
68 * Configured group does not exist.
70 public static final String GROUP_WRONG = "groupWrong";
72 * StructureManager in BridgeHandler is null
74 public static final String NO_STRUC_MAN = "noStrucMan";
76 * Configured scene is null.
78 public static final String NO_SCENE = "noScene";
80 * BridgeHandler is null.
82 public static final String NO_BRIDGE = "noBridge";
84 private BridgeHandler bridgeHandler;
85 private InternalScene scene;
86 private String sceneThingID;
89 * Creates a new {@link SceneHandler}.
91 * @param thing must not be null
93 public SceneHandler(Thing thing) {
98 public void initialize() {
99 logger.debug("Initializing SceneHandler");
100 final Bridge bridge = getBridge();
101 if (bridge != null) {
102 bridgeStatusChanged(bridge.getStatusInfo());
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);
120 public void handleRemoval() {
121 if (getBridgeHandler() != null) {
122 this.bridgeHandler.childThingRemoved(sceneThingID);
124 updateStatus(ThingStatus.REMOVED);
128 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
129 if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
130 if (getBridgeHandler() != null) {
132 String sceneID = getSceneID(getConfig(), bridgeHandler);
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.");
140 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
141 "Configured zone '" + getConfig().get(DigitalSTROMBindingConstants.ZONE_ID)
142 + "' does not exist, please check the configuration.");
145 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
146 "Configured group '" + getConfig().get(DigitalSTROMBindingConstants.GROUP_ID)
147 + "' does not exist, please check the configuration.");
150 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
151 "Waiting for building digitalSTROM model.");
154 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
155 "No Scene-ID is set!");
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);
165 updateStatus(ThingStatus.ONLINE);
168 updateStatus(ThingStatus.OFFLINE);
171 if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
174 logger.debug("Set status to {}", getThing().getStatusInfo());
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:
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>
192 * @param configuration (must not be null)
193 * @param bridgeHandler (can be null)
194 * @return unique Scene-ID or error string
196 public static String getSceneID(Configuration configuration, BridgeHandler bridgeHandler) {
197 if (configuration == null) {
198 throw new IllegalArgumentException("configuration cannot be null");
200 if (bridgeHandler == null) {
204 String configGroupID;
205 String configSceneID;
211 if (configuration.get(DigitalSTROMBindingConstants.ZONE_ID) != null) {
212 configZoneID = configuration.get(DigitalSTROMBindingConstants.ZONE_ID).toString();
216 if (configuration.get(DigitalSTROMBindingConstants.GROUP_ID) != null) {
217 configGroupID = configuration.get(DigitalSTROMBindingConstants.GROUP_ID).toString();
221 if (configuration.get(DigitalSTROMBindingConstants.SCENE_ID) != null) {
222 configSceneID = configuration.get(DigitalSTROMBindingConstants.SCENE_ID).toString();
226 if (!configSceneID.isEmpty()) {
228 sceneID = Short.parseShort(configSceneID);
229 if (!SceneEnum.containsScene(sceneID)) {
232 } catch (NumberFormatException e) {
234 sceneID = SceneEnum.valueOf(configSceneID.replace(" ", "_").toUpperCase()).getSceneNumber();
235 } catch (IllegalArgumentException e1) {
240 StructureManager strucMan = bridgeHandler.getStructureManager();
241 if (strucMan != null) {
242 if (configZoneID.isEmpty()) {
246 zoneID = Integer.parseInt(configZoneID);
247 if (!strucMan.checkZoneID(zoneID)) {
250 } catch (NumberFormatException e) {
251 zoneID = strucMan.getZoneId(configZoneID);
258 if (configGroupID.isEmpty()) {
262 groupID = Short.parseShort(configGroupID);
263 if (!strucMan.checkZoneGroupID(zoneID, groupID)) {
267 } catch (NumberFormatException e) {
268 String zoneName = strucMan.getZoneName(zoneID);
269 groupID = strucMan.getZoneGroupId(zoneName, configGroupID);
276 return zoneID + "-" + groupID + "-" + sceneID;
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.");
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);
298 this.bridgeHandler.sendSceneComandToDSS(scene, false);
302 logger.warn("Command sent to an unknown channel id: {}", channelUID);
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");
313 ThingHandler handler = bridge.getHandler();
315 if (handler instanceof BridgeHandler) {
316 this.bridgeHandler = (BridgeHandler) handler;
318 logger.debug("BridgeHandler cannot be found");
322 return this.bridgeHandler;
326 public void onSceneStateChanged(boolean flag) {
328 updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.ON);
330 updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.OFF);
335 public void channelLinked(ChannelUID channelUID) {
336 if (scene != null && channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
337 onSceneStateChanged(scene.isActive());
342 public void onSceneRemoved(InternalScene scene) {
344 updateStatus(ThingStatus.OFFLINE);
345 logger.debug("Set status on {}", getThing().getStatus());
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());
357 onSceneStateChanged(scene.isActive());
361 public String getSceneStatusListenerID() {
362 return this.sceneThingID;