/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.binding.digitalstrom.handler; import java.util.Set; import org.eclipse.smarthome.binding.digitalstrom.DigitalSTROMBindingConstants; import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.SceneStatusListener; import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.StructureManager; import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.InternalScene; import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; /** * The {@link SceneHandler} is responsible for handling commands, which are sent to the channel of an * DigitalSTROM-Scene.<br> * For that it uses the {@link BridgeHandler} to execute the actual command and implements the * {@link SceneStatusListener} to get informed about changes from the accompanying {@link InternalScene}. * * @author Michael Ochel - Initial contribution * @author Matthias Siegele - Initial contribution * */ public class SceneHandler extends BaseThingHandler implements SceneStatusListener { private Logger logger = LoggerFactory.getLogger(SceneHandler.class); public final static Set<ThingTypeUID> SUPPORTED_THING_TYPES = Sets.newHashSet( DigitalSTROMBindingConstants.THING_TYPE_APP_SCENE, DigitalSTROMBindingConstants.THING_TYPE_GROUP_SCENE, DigitalSTROMBindingConstants.THING_TYPE_ZONE_SCENE, DigitalSTROMBindingConstants.THING_TYPE_NAMED_SCENE); /** * Configured scene does not exist or cannot be used. */ public final static String SCENE_WRONG = "sceneWrong"; /** * Configured zone does not exist. */ public final static String ZONE_WRONG = "zoneWrong"; /** * Configured group does not exist. */ public final static String GROUP_WRONG = "groupWrong"; /** * StructureManager in BridgeHandler is null */ public final static String NO_STRUC_MAN = "noStrucMan"; /** * Configured scene is null. */ public final static String NO_SCENE = "noScene"; /** * BridgeHandler is null. */ public final static String NO_BRIDGE = "noBridge"; private BridgeHandler bridgeHandler = null; private InternalScene scene; private String sceneThingID = null; public SceneHandler(Thing thing) { super(thing); } @Override public void initialize() { logger.debug("Initializing SceneHandler"); if (getBridge() != null) { bridgeStatusChanged(getBridge().getStatusInfo()); } } @Override public void dispose() { logger.debug("Handler disposed... unregistering SceneStatusListener"); if (sceneThingID != null) { BridgeHandler dssBridgeHandler = getBridgeHandler(); if (dssBridgeHandler != null) { getBridgeHandler().unregisterSceneStatusListener(this); } sceneThingID = null; scene = null; } } @Override public void handleRemoval() { if (getBridgeHandler() != null) { this.bridgeHandler.childThingRemoved(sceneThingID); } updateStatus(ThingStatus.REMOVED); } @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) { if (getBridgeHandler() != null) { String sceneID = getSceneID(getConfig(), bridgeHandler); switch (sceneID) { case SCENE_WRONG: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configured scene '" + getConfig().get(DigitalSTROMBindingConstants.SCENE_ID) + "' does not exist or cannot be used, please check the configuration."); break; case ZONE_WRONG: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configured zone '" + getConfig().get(DigitalSTROMBindingConstants.SCENE_ZONE_ID) + "' does not exist, please check the configuration."); break; case GROUP_WRONG: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configured group '" + getConfig().get(DigitalSTROMBindingConstants.SCENE_GROUP_ID) + "' does not exist, please check the configuration."); break; case NO_STRUC_MAN: updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Waiting for building digitalSTROM model."); break; case NO_SCENE: updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No Scene-ID is set!"); break; default: this.sceneThingID = sceneID; updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Waiting for listener registration"); logger.debug("Set status on {}", getThing().getStatus()); this.bridgeHandler.registerSceneStatusListener(this); } } } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); } } /** * Checks the configuration and returns a unique Scene-ID or error string.<br> * The {@link StructureManager} of the {@link BridgeHandler} is used for checking the existing configured zone and * group. The {@link SceneEnum} will be used to check, if the configured scene exists and is allowed to use.<br> * If the check succeed the scene-ID will be returned in format "[zoneID]-[groupID]-[SceneID]", otherwise one of the * following errors {@link String}s will returned: * <li>{@link #SCENE_WRONG}: Configured scene does not exist or cannot be used.</li> * <li>{@link #ZONE_WRONG} Configured zone does not exist.</li> * <li>{@link #GROUP_WRONG}: Configured group does not exist.</li> * <li>{@link #NO_STRUC_MAN}: StructureManager in BridgeHandler is null.</li> * <li>{@link #NO_SCENE}: Configured scene is null.</li> * <li>{@link #NO_BRIDGE}: BridgeHandler is null.</li><br> * * @param configuration (must not be null) * @param bridgeHandler (can be null) * @return unique Scene-ID or error string */ public static String getSceneID(Configuration configuration, BridgeHandler bridgeHandler) { if (configuration == null) { throw new IllegalArgumentException("configuration cannot be null"); } if (bridgeHandler == null) { return NO_BRIDGE; } String configZoneID; String configGroupID; String configSceneID; short sceneID; int zoneID; short groupID; if (configuration.get(DigitalSTROMBindingConstants.SCENE_ZONE_ID) != null) { configZoneID = configuration.get(DigitalSTROMBindingConstants.SCENE_ZONE_ID).toString(); } else { configZoneID = ""; } if (configuration.get(DigitalSTROMBindingConstants.SCENE_GROUP_ID) != null) { configGroupID = configuration.get(DigitalSTROMBindingConstants.SCENE_GROUP_ID).toString(); } else { configGroupID = ""; } if (configuration.get(DigitalSTROMBindingConstants.SCENE_ID) != null) { configSceneID = configuration.get(DigitalSTROMBindingConstants.SCENE_ID).toString(); } else { configSceneID = ""; } if (!configSceneID.isEmpty()) { try { sceneID = Short.parseShort(configSceneID); if (!SceneEnum.containsScene(sceneID)) { return SCENE_WRONG; } } catch (NumberFormatException e) { try { sceneID = SceneEnum.valueOf(configSceneID.replace(" ", "_").toUpperCase()).getSceneNumber(); } catch (IllegalArgumentException e1) { return SCENE_WRONG; } } StructureManager strucMan = bridgeHandler.getStructureManager(); if (strucMan != null) { if (configZoneID.isEmpty()) { zoneID = 0; } else { try { zoneID = Integer.parseInt(configZoneID); if (!strucMan.checkZoneID(zoneID)) { return ZONE_WRONG; } } catch (NumberFormatException e) { zoneID = strucMan.getZoneId(configZoneID); if (zoneID == -1) { return ZONE_WRONG; } } } if (configGroupID.isEmpty()) { groupID = 0; } else { try { groupID = Short.parseShort(configGroupID); if (!strucMan.checkZoneGroupID(zoneID, groupID)) { return GROUP_WRONG; } } catch (NumberFormatException e) { String zoneName = strucMan.getZoneName(zoneID); groupID = strucMan.getZoneGroupId(zoneName, configGroupID); } if (groupID == -1) { return GROUP_WRONG; } } return zoneID + "-" + groupID + "-" + sceneID; } else { return NO_STRUC_MAN; } } else { return NO_SCENE; } } @Override public void handleCommand(ChannelUID channelUID, Command command) { BridgeHandler dssBridgeHandler = getBridgeHandler(); if (dssBridgeHandler == null) { logger.warn("BridgeHandler not found. Cannot handle command without bridge."); return; } if (channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) { if (command instanceof OnOffType) { if (OnOffType.ON.equals(command)) { this.bridgeHandler.sendSceneComandToDSS(scene, true); } else { this.bridgeHandler.sendSceneComandToDSS(scene, false); } } } else { logger.warn("Command sent to an unknown channel id: " + channelUID); } } private synchronized BridgeHandler getBridgeHandler() { if (this.bridgeHandler == null) { Bridge bridge = getBridge(); if (bridge == null) { logger.debug("Bridge cannot be found"); return null; } ThingHandler handler = bridge.getHandler(); if (handler instanceof BridgeHandler) { this.bridgeHandler = (BridgeHandler) handler; } else { logger.debug("BridgeHandler cannot be found"); return null; } } return this.bridgeHandler; } @Override public void onSceneStateChanged(boolean flag) { if (flag) { updateState(new ChannelUID(getThing().getUID(), DigitalSTROMBindingConstants.CHANNEL_ID_SCENE), OnOffType.ON); } else { updateState(new ChannelUID(getThing().getUID(), DigitalSTROMBindingConstants.CHANNEL_ID_SCENE), OnOffType.OFF); } } @Override public void channelLinked(ChannelUID channelUID) { if (scene != null && channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) { onSceneStateChanged(scene.isActive()); } } @Override public void onSceneRemoved(InternalScene scene) { scene = null; updateStatus(ThingStatus.OFFLINE); logger.debug("Set status on {}", getThing().getStatus()); } @Override public void onSceneAdded(InternalScene scene) { logger.debug("Scene {} added", scene.getID()); if (this.bridgeHandler != null) { ThingStatusInfo statusInfo = this.bridgeHandler.getThing().getStatusInfo(); updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription()); logger.debug("Set status on {}", getThing().getStatus()); } this.scene = scene; onSceneStateChanged(scene.isActive()); } @Override public String getSceneStatusListenerID() { return this.sceneThingID; } }