/**
* 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.internal.lib.manager.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.stateEnums.ManagerStates;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.stateEnums.ManagerTypes;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.SceneManager;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.StructureManager;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.devices.deviceParameters.DSID;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.SceneDiscovery;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.constants.EventPropertyEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.sceneEvent.EventItem;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.sceneEvent.EventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneManagerImpl} is the implementation of the {@link SceneManager}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public class SceneManagerImpl implements SceneManager {
private Logger logger = LoggerFactory.getLogger(SceneManagerImpl.class);
private List<String> echoBox = Collections.synchronizedList(new LinkedList<String>());
private Map<String, InternalScene> internalSceneMap = Collections
.synchronizedMap(new HashMap<String, InternalScene>());
private EventListener eventListener;
private StructureManager structureManager;
private ConnectionManager connectionManager;
private SceneDiscovery discovery;
private ManagerStatusListener statusListener = null;
private ManagerStates state = ManagerStates.STOPPED;
private boolean scenesGenerated = false;
public SceneManagerImpl(ConnectionManager connectionManager, StructureManager structureManager) {
this.structureManager = structureManager;
this.connectionManager = connectionManager;
this.discovery = new SceneDiscovery(this);
}
public SceneManagerImpl(ConnectionManager connectionManager, StructureManager structureManager,
ManagerStatusListener statusListener) {
this.structureManager = structureManager;
this.connectionManager = connectionManager;
this.discovery = new SceneDiscovery(this);
this.statusListener = statusListener;
}
@Override
public void start() {
if (eventListener == null) {
eventListener = new EventListener(connectionManager, this);
}
this.eventListener.start();
stateChanged(ManagerStates.RUNNING);
}
@Override
public void stop() {
if (this.eventListener != null) {
this.eventListener.stop();
this.eventListener = null;
}
this.discovery.stop();
this.stateChanged(ManagerStates.STOPPED);
}
@Override
public void handleEvent(EventItem eventItem) {
if (eventItem != null) {
boolean isCallScene = true;
String isCallStr = eventItem.getProperties().get(EventPropertyEnum.EVENT_NAME);
if (isCallStr != null) {
isCallScene = isCallStr.equals("callScene");
}
boolean isDeviceCall = false;
String deviceCallStr = eventItem.getProperties().get(EventPropertyEnum.IS_DEVICE_CALL);
if (deviceCallStr != null) {
isDeviceCall = deviceCallStr.equals("true");
}
if (isDeviceCall) {
String dsidStr = null;
dsidStr = eventItem.getProperties().get(EventPropertyEnum.DSID);
short sceneId = -1;
String sceneStr = eventItem.getProperties().get(EventPropertyEnum.SCENEID);
if (sceneStr != null) {
try {
sceneId = Short.parseShort(sceneStr);
} catch (java.lang.NumberFormatException e) {
logger.error("An exception occurred, while handling event at parsing sceneID: " + sceneStr, e);
}
}
if (!isEcho(dsidStr, sceneId)) {
if (isCallScene) {
this.callDeviceScene(dsidStr, sceneId);
} else {
this.undoDeviceScene(dsidStr);
}
}
} else {
String intSceneID = null;
String zoneIDStr = eventItem.getProperties().get(EventPropertyEnum.ZONEID);
String sceneIDStr = eventItem.getProperties().get(EventPropertyEnum.SCENEID);
String groupIDStr = eventItem.getProperties().get(EventPropertyEnum.GROUPID);
if (zoneIDStr != null && sceneIDStr != null && groupIDStr != null) {
intSceneID = zoneIDStr + "-" + groupIDStr + "-" + sceneIDStr;
if (!isEcho(intSceneID)) {
if (isCallScene) {
this.callInternalScene(intSceneID);
} else {
this.undoInternalScene(intSceneID);
}
}
}
}
}
}
private boolean isEcho(String dsid, short sceneId) {
// sometimes the dS-event have a dSUID saved in the dSID
if (structureManager.getDeviceByDSUID(dsid) != null) {
dsid = structureManager.getDeviceByDSUID(dsid).getDSID().getValue();
}
String echo = dsid + "-" + sceneId;
return isEcho(echo);
}
private boolean isEcho(String echoID) {
if (echoBox.contains(echoID)) {
echoBox.remove(echoID);
return true;
}
return false;
}
// ... we want to ignore own 'command-echos'
@Override
public void addEcho(String dsid, short sceneId) {
addEcho(dsid + "-" + sceneId);
}
// ... we want to ignore own 'command-echos'
@Override
public void addEcho(String internalSceneID) {
echoBox.add(internalSceneID);
}
@Override
public void callInternalScene(InternalScene scene) {
InternalScene intScene = this.internalSceneMap.get(scene.getID());
if (intScene != null) {
intScene.activateScene();
} else {
if (SceneEnum.getScene(scene.getSceneID()) != null
&& structureManager.checkZoneGroupID(scene.getZoneID(), scene.getGroupID())) {
scene.addReferenceDevices(this.structureManager.getReferenceDeviceListFromZoneXGroupX(scene.getZoneID(),
scene.getGroupID()));
this.internalSceneMap.put(scene.getID(), scene);
}
}
}
@Override
public void callInternalScene(String sceneID) {
InternalScene intScene = this.internalSceneMap.get(sceneID);
if (intScene != null) {
intScene.activateScene();
} else {
intScene = createNewScene(sceneID);
if (intScene != null) {
discovery.sceneDiscoverd(intScene);
intScene.activateScene();
}
}
}
@Override
public void addInternalScene(InternalScene intScene) {
if (!this.internalSceneMap.containsKey(intScene.getID())) {
if (SceneEnum.getScene(intScene.getSceneID()) != null
&& structureManager.checkZoneGroupID(intScene.getZoneID(), intScene.getGroupID())) {
intScene.addReferenceDevices(this.structureManager
.getReferenceDeviceListFromZoneXGroupX(intScene.getZoneID(), intScene.getGroupID()));
this.internalSceneMap.put(intScene.getID(), intScene);
}
} else {
InternalScene oldScene = this.internalSceneMap.get(intScene.getID());
String oldSceneName = this.internalSceneMap.get(intScene.getID()).getSceneName();
String newSceneName = intScene.getSceneName();
if ((oldSceneName.contains("Zone:") && oldSceneName.contains("Group:") && oldSceneName.contains("Scene:"))
&& !(newSceneName.contains("Zone:") && newSceneName.contains("Group:")
&& newSceneName.contains("Scene:"))) {
oldScene.setSceneName(newSceneName);
this.discovery.sceneDiscoverd(oldScene);
}
}
}
@Override
public void removeInternalScene(String sceneID) {
this.internalSceneMap.remove(sceneID);
}
@Override
public InternalScene getInternalScene(String sceneID) {
return this.internalSceneMap.get(sceneID);
}
private InternalScene createNewScene(String sceneID) {
String[] sceneData = sceneID.split("-");
if (sceneData.length == 3) {
int zoneID = Integer.parseInt(sceneData[0]);
short groupID = Short.parseShort(sceneData[1]);
short sceneNumber = Short.parseShort(sceneData[2]);
String sceneName = null;
if (connectionManager.checkConnection()) {
sceneName = connectionManager.getDigitalSTROMAPI().getSceneName(connectionManager.getSessionToken(),
zoneID, groupID, sceneNumber);
}
InternalScene intScene = null;
if (SceneEnum.getScene(sceneNumber) != null && structureManager.checkZoneGroupID(zoneID, groupID)) {
if (sceneName == null) {
if (structureManager.getZoneName(zoneID) != null) {
sceneName = "Zone: " + structureManager.getZoneName(zoneID);
if (structureManager.getZoneGroupName(zoneID, groupID) != null) {
sceneName = sceneName + " Group: " + structureManager.getZoneGroupName(zoneID, groupID);
} else {
sceneName = sceneName + " Group: " + groupID;
}
} else {
if (structureManager.getZoneGroupName(zoneID, groupID) != null) {
sceneName = "Zone: " + zoneID + " Group: "
+ structureManager.getZoneGroupName(zoneID, groupID);
} else {
sceneName = "Zone: " + zoneID + " Group: " + groupID;
}
}
sceneName = sceneName + " Scene: "
+ SceneEnum.getScene(sceneNumber).toString().toLowerCase().replace("_", " ");
}
intScene = new InternalScene(zoneID, groupID, sceneNumber, sceneName);
}
return intScene;
}
return null;
}
@Override
public void callDeviceScene(String dSID, Short sceneID) {
Device device = this.structureManager.getDeviceByDSID(new DSID(dSID));
if (device != null) {
device.internalCallScene(sceneID);
} else {
device = this.structureManager.getDeviceByDSUID(dSID);
if (device != null) {
device.internalCallScene(sceneID);
}
}
}
@Override
public void callDeviceScene(Device device, Short sceneID) {
if (device != null) {
callDeviceScene(device.getDSID().toString(), sceneID);
}
}
@Override
public void undoInternalScene(InternalScene scene) {
if (scene != null) {
undoInternalScene(scene.getID());
}
}
@Override
public void undoInternalScene(String sceneID) {
InternalScene intScene = this.internalSceneMap.get(sceneID);
if (intScene != null) {
intScene.deactivateScene();
} else {
intScene = createNewScene(sceneID);
if (intScene != null) {
intScene.deactivateScene();
}
}
}
@Override
public void undoDeviceScene(String dSID) {
Device device = this.structureManager.getDeviceByDSID(new DSID(dSID));
if (device != null) {
device.internalUndoScene();
} else {
device = this.structureManager.getDeviceByDSUID(dSID);
if (device != null) {
device.internalUndoScene();
}
}
}
@Override
public void undoDeviceScene(Device device) {
if (device != null) {
undoDeviceScene(device.getDSID().toString());
}
}
@Override
public void registerSceneListener(SceneStatusListener sceneListener) {
if (sceneListener != null) {
String id = sceneListener.getSceneStatusListenerID();
if (id.equals(SceneStatusListener.SCENE_DISCOVERY)) {
discovery.registerSceneDiscovery(sceneListener);
for (InternalScene scene : internalSceneMap.values()) {
discovery.sceneDiscoverd(scene);
}
} else {
InternalScene intScene = internalSceneMap.get(sceneListener.getSceneStatusListenerID());
if (intScene != null) {
intScene.registerSceneListener(sceneListener);
} else {
addInternalScene(createNewScene(id));
registerSceneListener(sceneListener);
}
}
}
}
@Override
public void unregisterSceneListener(SceneStatusListener sceneListener) {
if (sceneListener != null) {
String id = sceneListener.getSceneStatusListenerID();
if (id.equals(SceneStatusListener.SCENE_DISCOVERY)) {
this.discovery.unRegisterDiscovery();
} else {
InternalScene intScene = this.internalSceneMap.get(sceneListener.getSceneStatusListenerID());
if (intScene != null) {
intScene.unregisterSceneListener();
}
}
}
}
@Override
public synchronized boolean scenesGenerated() {
return scenesGenerated;
}
@Override
public void generateScenes() {
stateChanged(ManagerStates.GENERATING_SCENES);
logger.debug("start generating scenes");
discovery.generateAllScenes(connectionManager, structureManager);
}
@Override
public void scenesGenerated(char[] scenesGenerated) {
if (String.valueOf(scenesGenerated).equals("1111")) {
this.scenesGenerated = true;
stateChanged(ManagerStates.RUNNING);
}
if (String.valueOf(scenesGenerated).contains("2")) {
stateChanged(ManagerStates.RUNNING);
}
}
@Override
public boolean isDiscoveryRegistrated() {
return this.discovery != null;
}
private void stateChanged(ManagerStates state) {
this.state = state;
if (statusListener != null) {
statusListener.onStatusChanged(ManagerTypes.SCENE_MANAGER, state);
}
}
@Override
public ManagerTypes getManagerType() {
return ManagerTypes.SCENE_MANAGER;
}
@Override
public synchronized ManagerStates getManagerState() {
return state;
}
@Override
public List<InternalScene> getScenes() {
return this.internalSceneMap != null ? new LinkedList<InternalScene>(this.internalSceneMap.values()) : null;
}
@Override
public void registerStatusListener(ManagerStatusListener statusListener) {
this.statusListener = statusListener;
}
@Override
public void unregisterStatusListener() {
this.statusListener = null;
}
}