/**
* 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.structure.scene;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.config.Config;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
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.serverConnection.constants.JSONApiResponseKeysEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.serverConnection.impl.JSONResponseHandler;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.devices.deviceParameters.FunctionalColorGroupEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.constants.ApartmentSceneEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.constants.ZoneSceneEnum;
import org.eclipse.smarthome.core.common.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link SceneDiscovery} can read out various digitalSTROM-Scene types and generates a list of theirs or manages it
* by the {@link SceneManager}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class SceneDiscovery {
private static final Logger logger = LoggerFactory.getLogger(SceneDiscovery.class);
private char[] scenesGenerated = "0000".toCharArray();
private List<InternalScene> namedScenes = new LinkedList<InternalScene>();
private boolean genList = false;
ScheduledFuture<?> generateReachableScenesScheduledFuture = null;
private SceneManager sceneManager;
private SceneStatusListener discovery = null;
private final String query = "/json/property/query?query=/apartment/zones/*(ZoneID)/groups/*(group)/scenes/*(scene,name)";
private final String reachableScenesQuery = "/json/zone/getReachableScenes?id=";
private final String reachableGroupsQuery = "/json/apartment/getReachableGroups?token=";
/**
* Creates a new {@link SceneDiscovery} with managed scene by the {@link SceneManager}
*
* @param sceneManager must not be null
*/
public SceneDiscovery(SceneManager sceneManager) {
this.sceneManager = sceneManager;
}
/**
* Creates a new {@link SceneDiscovery} and generates only a list of all scenes, if genList is true.
*
* @param genList
*/
public SceneDiscovery(boolean genList) {
this.genList = genList;
}
/**
* Generates all named, reachable group, apartment and zone scenes.
*
* @param connectionManager must not be null
* @param structureManager must not be null
*/
public void generateAllScenes(ConnectionManager connectionManager, StructureManager structureManager) {
generateNamedScenes(connectionManager);
generateApartmentScence();
generateZoneScenes(connectionManager, structureManager);
generateReachableScenes(connectionManager, structureManager);
}
/**
* Generates all named scenes.
*
* @param connectionManager must not be null
* @return true, if successful otherwise false
*/
public boolean generateNamedScenes(ConnectionManager connectionManager) {
if (connectionManager.checkConnection()) {
String response = connectionManager.getHttpTransport()
.execute(query + "&token=" + connectionManager.getSessionToken());
if (response == null) {
return false;
} else {
JsonObject responsJsonObj = JSONResponseHandler.toJsonObject(response);
if (JSONResponseHandler.checkResponse(responsJsonObj)) {
addScenesToList(JSONResponseHandler.getResultJsonObject(responsJsonObj));
scenesGenerated[0] = '1';
sceneManager.scenesGenerated(scenesGenerated);
return true;
}
}
}
scenesGenerated[0] = '2';
sceneManager.scenesGenerated(scenesGenerated);
return false;
}
/**
* Generates all apartment scenes.
*/
public void generateApartmentScence() {
for (ApartmentSceneEnum apartmentScene : ApartmentSceneEnum.values()) {
InternalScene scene = new InternalScene(null, null, apartmentScene.getSceneNumber(),
"Apartment-Scene: " + apartmentScene.toString().toLowerCase().replace("_", " "));
if (genList) {
this.namedScenes.add(scene);
} else {
sceneDiscoverd(scene);
}
}
scenesGenerated[1] = '1';
sceneManager.scenesGenerated(scenesGenerated);
}
private void addScenesToList(JsonObject resultJsonObj) {
if (resultJsonObj.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES.getKey()) instanceof JsonArray) {
JsonArray zones = (JsonArray) resultJsonObj
.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES.getKey());
for (int i = 0; i < zones.size(); i++) {
if (((JsonObject) zones.get(i)).get(
JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES_GROUPS.getKey()) instanceof JsonArray) {
JsonArray groups = (JsonArray) ((JsonObject) zones.get(i))
.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES_GROUPS.getKey());
for (int j = 0; j < groups.size(); j++) {
if (((JsonObject) groups.get(j)).get("scenes") instanceof JsonArray) {
JsonArray scenes = (JsonArray) ((JsonObject) groups.get(j)).get("scenes");
for (int k = 0; k < scenes.size(); k++) {
if (scenes.get(k).isJsonObject()) {
JsonObject sceneJsonObject = ((JsonObject) scenes.get(k));
int zoneID = ((JsonObject) zones.get(i)).get("ZoneID").getAsInt();
short groupID = ((JsonObject) groups.get(j)).get("group").getAsShort();
InternalScene scene = new InternalScene(zoneID, groupID,
sceneJsonObject.get("scene").getAsShort(),
sceneJsonObject.get("name").getAsString());
if (genList) {
this.namedScenes.add(scene);
} else {
sceneDiscoverd(scene);
}
}
}
}
}
}
}
}
}
/**
* Generates all zone scenes.
*
* @param connectionManager must not be null
* @param structureManager must not be null
* @return success true otherwise false
*/
public boolean generateZoneScenes(ConnectionManager connectionManager, StructureManager structureManager) {
HashMap<Integer, List<Short>> reachableGroups = getReachableGroups(connectionManager);
if (reachableGroups != null) {
for (Integer zoneID : reachableGroups.keySet()) {
if (!reachableGroups.get(zoneID).isEmpty()) {
for (ZoneSceneEnum zoneScene : ZoneSceneEnum.values()) {
String sceneName = "Zone-Scene: Zone: ";
if (structureManager.getZoneName(zoneID) != null
&& !structureManager.getZoneName(zoneID).isEmpty()) {
sceneName = sceneName + structureManager.getZoneName(zoneID);
} else {
sceneName = sceneName + zoneID;
}
sceneName = sceneName + " Scene: " + zoneScene.toString().toLowerCase().replace("_", " ");
InternalScene scene = new InternalScene(zoneID, null, zoneScene.getSceneNumber(), sceneName);
if (genList) {
this.namedScenes.add(scene);
} else {
sceneDiscoverd(scene);
}
}
}
}
scenesGenerated[2] = '1';
sceneManager.scenesGenerated(scenesGenerated);
return true;
}
scenesGenerated[2] = '2';
sceneManager.scenesGenerated(scenesGenerated);
return false;
}
/**
*
*/
public void stop() {
if (generateReachableScenesScheduledFuture != null) {
generateReachableScenesScheduledFuture.cancel(true);
generateReachableScenesScheduledFuture = null;
}
}
/**
* Generates all reachable scenes.
*
* @param connectionManager
* @param structureManager
*/
public void generateReachableScenes(final ConnectionManager connectionManager,
final StructureManager structureManager) {
if (generateReachableScenesScheduledFuture == null || generateReachableScenesScheduledFuture.isCancelled()) {
generateReachableScenesScheduledFuture = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME)
.scheduleAtFixedRate(new Runnable() {
HashMap<Integer, List<Short>> reachableGroups = getReachableGroups(connectionManager);
Iterator<Integer> zoneIdInter = null;
Iterator<Short> groupIdInter = null;
Integer zoneID = null;
@Override
public void run() {
if (reachableGroups != null) {
if (zoneIdInter == null) {
zoneIdInter = reachableGroups.keySet().iterator();
}
if (groupIdInter == null) {
if (zoneIdInter.hasNext()) {
zoneID = zoneIdInter.next();
groupIdInter = reachableGroups.get(zoneID).iterator();
} else {
zoneID = null;
scenesGenerated[3] = '1';
sceneManager.scenesGenerated(scenesGenerated);
generateReachableScenesScheduledFuture.cancel(true);
}
}
if (zoneID != null) {
if (groupIdInter != null) {
if (connectionManager.checkConnection()) {
Short groupID = null;
if (groupIdInter.hasNext()) {
groupID = groupIdInter.next();
} else {
groupIdInter = null;
}
if (groupID != null) {
if (FunctionalColorGroupEnum.getColorGroup((int) groupID)
.equals(FunctionalColorGroupEnum.YELLOW)) {
discoverScene(SceneEnum.AUTO_OFF.getSceneNumber(), groupID);
}
String response = connectionManager.getHttpTransport()
.execute(reachableScenesQuery + zoneID + "&groupID=" + groupID
+ "&token=" + connectionManager.getSessionToken());
if (response == null) {
scenesGenerated[3] = '2';
sceneManager.scenesGenerated(scenesGenerated);
return;
} else {
JsonObject responsJsonObj = JSONResponseHandler
.toJsonObject(response);
if (JSONResponseHandler.checkResponse(responsJsonObj)) {
JsonObject resultJsonObj = JSONResponseHandler
.getResultJsonObject(responsJsonObj);
if (resultJsonObj
.get(JSONApiResponseKeysEnum.ZONE_GET_REACHABLE_SCENES
.getKey()) instanceof JsonArray) {
JsonArray scenes = (JsonArray) resultJsonObj
.get(JSONApiResponseKeysEnum.ZONE_GET_REACHABLE_SCENES
.getKey());
if (scenes != null) {
for (int i = 0; i < scenes.size(); i++) {
discoverScene(scenes.get(i).getAsShort(), groupID);
}
}
}
}
}
}
} else {
scenesGenerated[3] = '2';
sceneManager.scenesGenerated(scenesGenerated);
generateReachableScenesScheduledFuture.cancel(true);
}
}
}
}
}
private void discoverScene(short sceneNumber, short groupID) {
String sceneName = null;
if (SceneEnum.getScene(sceneNumber) != null) {
if (structureManager.getZoneName(zoneID) != null
&& !structureManager.getZoneName(zoneID).isEmpty()) {
sceneName = "Zone: " + structureManager.getZoneName(zoneID);
} else {
sceneName = "Zone: " + zoneID;
}
if (structureManager.getZoneGroupName(zoneID, groupID) != null
&& !structureManager.getZoneGroupName(zoneID, groupID).isEmpty()) {
sceneName = sceneName + " Group: "
+ structureManager.getZoneGroupName(zoneID, groupID);
} else {
sceneName = sceneName + " Group: " + groupID;
}
sceneName = sceneName + " Scene: "
+ SceneEnum.getScene(sceneNumber).toString().toLowerCase().replace("_", " ");
}
InternalScene scene = new InternalScene(zoneID, groupID, sceneNumber, sceneName);
if (genList) {
namedScenes.add(scene);
} else {
sceneDiscoverd(scene);
}
}
}, 0, 500, TimeUnit.MILLISECONDS);
}
}
private HashMap<Integer, List<Short>> getReachableGroups(ConnectionManager connectionManager) {
HashMap<Integer, List<Short>> reachableGroupsMap = null;
if (connectionManager.checkConnection()) {
String response = connectionManager.getHttpTransport()
.execute(this.reachableGroupsQuery + connectionManager.getSessionToken());
if (response == null) {
return null;
} else {
JsonObject responsJsonObj = JSONResponseHandler.toJsonObject(response);
if (JSONResponseHandler.checkResponse(responsJsonObj)) {
JsonObject resultJsonObj = JSONResponseHandler.getResultJsonObject(responsJsonObj);
if (resultJsonObj
.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES.getKey()) instanceof JsonArray) {
JsonArray zones = (JsonArray) resultJsonObj
.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES.getKey());
reachableGroupsMap = new HashMap<Integer, List<Short>>(zones.size());
List<Short> groupList;
for (int i = 0; i < zones.size(); i++) {
if (((JsonObject) zones.get(i))
.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES_GROUPS
.getKey()) instanceof JsonArray) {
JsonArray groups = (JsonArray) ((JsonObject) zones.get(i))
.get(JSONApiResponseKeysEnum.APARTMENT_GET_STRUCTURE_ZONES_GROUPS.getKey());
groupList = new LinkedList<Short>();
for (int k = 0; k < groups.size(); k++) {
groupList.add(groups.get(k).getAsShort());
}
reachableGroupsMap.put(((JsonObject) zones.get(i)).get("zoneID").getAsInt(), groupList);
}
}
}
}
}
}
return reachableGroupsMap;
}
/**
* Informs the registered {@link SceneStausListener} as scene discovery about a new scene.
*
* @param scene
*/
public void sceneDiscoverd(InternalScene scene) {
if (scene != null) {
if (SceneEnum.containsScene(scene.getSceneID())) {
if (!isStandardScene(scene.getSceneID())) {
if (this.discovery != null) {
this.discovery.onSceneAdded(scene);
logger.debug("Inform scene discovery about added scene with id: " + scene.getID());
} else {
logger.debug("Can't inform scene discovery about added scene with id: " + scene.getID()
+ " because scene discovery is disabled");
}
}
this.sceneManager.addInternalScene(scene);
} else {
logger.error("Added scene with id: " + scene.getID() + " is a not usage scene!");
}
}
}
private boolean isStandardScene(short sceneID) {
switch (SceneEnum.getScene(sceneID)) {
case INCREMENT:
case DECREMENT:
case STOP:
case MINIMUM:
case MAXIMUM:
case AUTO_OFF:
case DEVICE_ON:
case DEVICE_OFF:
case DEVICE_STOP:
return true;
default:
return false;
}
}
/**
* Registers the given {@link SceneStatusListener} as scene discovery.
*
* @param listener
*/
public void registerSceneDiscovery(SceneStatusListener listener) {
this.discovery = listener;
}
/**
* Unregisters the {@link SceneStatusListener} as scene discovery from this {@link InternalScene}.
*/
public void unRegisterDiscovery() {
this.discovery = null;
}
/**
* Returns the list of all generated {@link InternalScene}'s, if the list shall generated.
*
* @return List of all {@link InternalScene} or null
*/
public List<InternalScene> getNamedSceneList() {
if (genList) {
return this.namedScenes;
} else {
if (sceneManager != null) {
sceneManager.getScenes();
}
}
return null;
}
@Override
public String toString() {
return this.namedScenes.toString();
}
}