/**
* 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 static org.eclipse.smarthome.binding.digitalstrom.DigitalSTROMBindingConstants.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.eclipse.smarthome.binding.digitalstrom.DigitalSTROMBindingConstants;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.config.Config;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.ConnectionListener;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
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.TotalPowerConsumptionListener;
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.DeviceStatusManager;
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.manager.impl.ConnectionManagerImpl;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.impl.DeviceStatusManagerImpl;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.impl.SceneManagerImpl;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.manager.impl.StructureManagerImpl;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.devices.deviceParameters.DeviceStateUpdate;
import org.eclipse.smarthome.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.core.library.types.DecimalType;
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.ThingTypeUID;
import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.eclipse.smarthome.core.thing.binding.builder.ThingStatusInfoBuilder;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BridgeHandler} is the handler for a digitalSTROM-Server and connects it to
* the framework.<br>
* All {@link DeviceHandler}s and {@link SceneHandler}s use the {@link BridgeHandler} to execute the actual
* commands.
* <p>
* The {@link BridgeHandler} also:
* <ul>
* <li>manages the {@link DeviceStatusManager} (starts, stops, register {@link DeviceStatusListener},
* register {@link SceneStatusListener} and so on)</li>
* <li>creates and load the configurations in the {@link Config}.</li>
* <li>implements {@link ManagerStatusListener} to manage the expiration of the Thing initializations</li>
* <li>implements the {@link ConnectionListener} to manage the {@link ThingStatus} of this {@link BridgeHandler}</li>
* <li>and implements the {@link TotalPowerConsumptionListener} to update his Channels.</li>
* </ul>
* </p>
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class BridgeHandler extends BaseBridgeHandler
implements ConnectionListener, TotalPowerConsumptionListener, ManagerStatusListener {
private Logger logger = LoggerFactory.getLogger(BridgeHandler.class);
public final static Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DSS_BRIDGE);
/* DS-Manager */
private ConnectionManager connMan;
private StructureManager structMan;
private SceneManager sceneMan;
private DeviceStatusManager devStatMan;
private List<SceneStatusListener> sceneListener;
private List<DeviceStatusListener> devListener;
private Config config = null;
List<SceneStatusListener> unregisterSceneStatusListeners = null;
private boolean generatingScenes = false;
private int connectionTimeoutCounter = 0;
private class Initializer implements Runnable {
BridgeHandler bridge = null;
Config config;
public Initializer(BridgeHandler bridge, Config config) {
this.bridge = bridge;
this.config = config;
}
@Override
public void run() {
logger.info("Checking connection");
if (connMan == null) {
connMan = new ConnectionManagerImpl(config, bridge, true);
} else {
connMan.registerConnectionListener(bridge);
connMan.configHasBeenUpdated();
}
logger.info("Initializing digitalSTROM Manager");
if (structMan == null) {
structMan = new StructureManagerImpl();
}
if (sceneMan == null) {
sceneMan = new SceneManagerImpl(connMan, structMan);
}
if (devStatMan == null) {
devStatMan = new DeviceStatusManagerImpl(connMan, structMan, sceneMan, bridge);
} else {
devStatMan.registerStatusListener(bridge);
}
structMan.generateZoneGroupNames(connMan);
devStatMan.registerTotalPowerConsumptionListener(bridge);
if (connMan.checkConnection()) {
devStatMan.start();
}
boolean configChanged = false;
Configuration configuration = bridge.getConfig();
if (connMan.getApplicationToken() != null) {
configuration.remove(USER_NAME);
configuration.remove(PASSWORD);
logger.debug("Application-Token is: " + connMan.getApplicationToken());
configuration.put(APPLICATION_TOKEN, connMan.getApplicationToken());
configChanged = true;
}
if (StringUtils.isNotBlank((String) configuration.get(DigitalSTROMBindingConstants.DS_NAME))
&& connMan.checkConnection()) {
String dSSname = connMan.getDigitalSTROMAPI().getInstallationName(connMan.getSessionToken());
if (dSSname != null) {
configuration.put(DS_NAME, dSSname);
}
}
if (configChanged) {
updateConfiguration(configuration);
}
if (StringUtils.isBlank(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT))
&& StringUtils.isNotBlank(config.getCert())) {
updateProperty(DigitalSTROMBindingConstants.SERVER_CERT, config.getCert());
}
}
};
public BridgeHandler(Bridge bridge) {
super(bridge);
}
@Override
public void initialize() {
logger.debug("Initializing digitalSTROM-BridgeHandler");
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Checking configuration...");
// Start an extra thread to readout the configuration and check the connection, because it takes sometimes more
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
Config config = loadAndCheckConfig();
if (config != null) {
logger.debug(config.toString());
scheduler.execute(new Initializer(this, config));
}
}
private boolean checkLoginConfig(Config config) {
if ((StringUtils.isNotBlank(config.getUserName()) && StringUtils.isNotBlank(config.getPassword()))
|| StringUtils.isNotBlank(config.getAppToken())) {
return true;
}
onConnectionStateChange(CONNECTION_LOST, NO_USER_PASSWORD);
return false;
}
private Config loadAndCheckConfig() {
Configuration thingConfig = super.getConfig();
Config config = loadAndCheckConnectionData(thingConfig);
if (config == null) {
return null;
}
logger.info("Loading configuration");
ArrayList<String> numberExc = null;
// Parameters can't be null, because of an existing default value.
if (StringUtils
.isNotBlank(thingConfig.get(DigitalSTROMBindingConstants.SENSOR_DATA_UPDATE_INTERVAL).toString())) {
try {
config.setSensordataRefreshInterval(Integer.parseInt(
thingConfig.get(DigitalSTROMBindingConstants.SENSOR_DATA_UPDATE_INTERVAL).toString() + "000"));
} catch (NumberFormatException e) {
if (numberExc == null) {
numberExc = new ArrayList<String>();
}
numberExc.add("\"Sensor update interval\"");
}
}
if (StringUtils
.isNotBlank(thingConfig.get(DigitalSTROMBindingConstants.TOTAL_POWER_UPDATE_INTERVAL).toString())) {
try {
config.setTotalPowerUpdateInterval(Integer.parseInt(
thingConfig.get(DigitalSTROMBindingConstants.TOTAL_POWER_UPDATE_INTERVAL).toString() + "000"));
} catch (NumberFormatException e) {
if (numberExc == null) {
numberExc = new ArrayList<String>();
}
numberExc.add("\"Total power update interval\"");
}
}
if (StringUtils.isNotBlank(thingConfig.get(DigitalSTROMBindingConstants.SENSOR_WAIT_TIME).toString())) {
try {
config.setSensorReadingWaitTime(Integer
.parseInt(thingConfig.get(DigitalSTROMBindingConstants.SENSOR_WAIT_TIME).toString() + "000"));
} catch (NumberFormatException e) {
if (numberExc == null) {
numberExc = new ArrayList<String>();
}
numberExc.add("\"Wait time sensor reading\"");
}
}
if (StringUtils.isNotBlank(
thingConfig.get(DigitalSTROMBindingConstants.DEFAULT_TRASH_DEVICE_DELETE_TIME_KEY).toString())) {
try {
config.setTrashDeviceDeleteTime(Integer.parseInt(
thingConfig.get(DigitalSTROMBindingConstants.DEFAULT_TRASH_DEVICE_DELETE_TIME_KEY).toString()));
} catch (NumberFormatException e) {
if (numberExc == null) {
numberExc = new ArrayList<String>();
}
numberExc.add("\"Days to be slaked trash bin devices\"");
}
}
if (numberExc != null) {
String excText = "The field ";
for (int i = 0; i < numberExc.size(); i++) {
excText = excText + numberExc.get(i);
if (i < numberExc.size() - 2) {
excText = excText + ", ";
} else if (i < numberExc.size() - 1) {
excText = excText + " and ";
}
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, excText + " have to be a number.");
return null;
}
if (StringUtils.isNotBlank(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT))) {
config.setCert(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT));
}
return config;
}
private Config loadAndCheckConnectionData(Configuration thingConfig) {
if (this.config == null) {
this.config = new Config();
}
// load and check connection and authorization data
if (StringUtils.isNotBlank((String) thingConfig.get(HOST))) {
config.setHost(thingConfig.get(HOST).toString());
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"The connection to the digitalSTROM-Server can't established, because the host address is missing. Please set the host address.");
return null;
}
if (thingConfig.get(USER_NAME) != null) {
config.setUserName(thingConfig.get(USER_NAME).toString());
}
if (thingConfig.get(PASSWORD) != null) {
config.setPassword(thingConfig.get(PASSWORD).toString());
}
if (thingConfig.get(APPLICATION_TOKEN) != null) {
config.setAppToken(thingConfig.get(APPLICATION_TOKEN).toString());
}
if (!checkLoginConfig(config)) {
return null;
}
return config;
}
@Override
public void dispose() {
logger.debug("Handler disposed");
if (devStatMan != null) {
devStatMan.unregisterTotalPowerConsumptionListener();
devStatMan.unregisterStatusListener();
this.devStatMan.stop();
}
if (connMan != null) {
connMan.unregisterConnectionListener();
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
switch (channelUID.getId()) {
case CHANNEL_ID_TOTAL_ACTIVE_POWER:
updateState(channelUID, new DecimalType(devStatMan.getTotalPowerConsumption()));
break;
case CHANNEL_ID_TOTAL_ELECTRIC_METER:
updateState(channelUID, new DecimalType(devStatMan.getTotalEnergyMeterValue()));
break;
default:
logger.debug("Command received for an unknown channel: {}", channelUID.getId());
break;
}
} else {
logger.debug("Command {} is not supported for channel: {}", command, channelUID.getId());
}
}
@Override
public void handleRemoval() {
for (Thing thing : getThing().getThings()) {
// Inform Thing-Child's about removed bridge.
thing.getHandler().bridgeStatusChanged(ThingStatusInfoBuilder.create(ThingStatus.REMOVED).build());
}
if (StringUtils.isNotBlank((String) super.getConfig().get(APPLICATION_TOKEN))) {
if (connMan == null) {
Config config = loadAndCheckConnectionData(this.getConfig());
if (config != null) {
this.connMan = new ConnectionManagerImpl(config, null, false);
} else {
updateStatus(ThingStatus.REMOVED);
return;
}
}
if (connMan.removeApplicationToken()) {
logger.debug("Application-Token deleted");
}
}
updateStatus(ThingStatus.REMOVED);
}
/* methods to store listener */
/**
* Registers a new {@link DeviceStatusListener} on the {@link DeviceStatusManager}.
*
* @param deviceStatusListener (must not be null)
*/
public synchronized void registerDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
if (this.devStatMan != null) {
if (deviceStatusListener == null) {
throw new IllegalArgumentException("It's not allowed to pass null.");
}
if (deviceStatusListener.getDeviceStatusListenerID() != null) {
devStatMan.registerDeviceListener(deviceStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a DeviceStatusListener with ID = null.");
}
} else {
devListener = new LinkedList<DeviceStatusListener>();
devListener.add(deviceStatusListener);
}
}
/**
* Unregisters a new {@link DeviceStatusListener} on the {@link BridgeHandler}.
*
* @param devicetatusListener (must not be null)
*/
public void unregisterDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
if (this.devStatMan != null) {
if (deviceStatusListener.getDeviceStatusListenerID() != null) {
this.devStatMan.unregisterDeviceListener(deviceStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a DeviceStatusListener with ID = null.");
}
}
}
/**
* Registers a new {@link SceneStatusListener} on the {@link BridgeHandler}.
*
* @param deviceStatusListener (must not be null)
*/
public synchronized void registerSceneStatusListener(SceneStatusListener sceneStatusListener) {
if (this.sceneMan != null && !generatingScenes) {
if (sceneStatusListener == null) {
throw new IllegalArgumentException("It's not allowed to pass null.");
}
if (sceneStatusListener.getSceneStatusListenerID() != null) {
this.sceneMan.registerSceneListener(sceneStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a SceneStatusListener with ID = null.");
}
} else {
if (sceneListener == null) {
sceneListener = new LinkedList<SceneStatusListener>();
}
sceneListener.add(sceneStatusListener);
}
}
/**
* Unregisters a new {@link SceneStatusListener} on the {@link DeviceStatusManager}.
*
* @param sceneStatusListener (must not be null)
*/
public void unregisterSceneStatusListener(SceneStatusListener sceneStatusListener) {
if (this.sceneMan != null && !generatingScenes) {
if (sceneStatusListener.getSceneStatusListenerID() != null) {
this.sceneMan.unregisterSceneListener(sceneStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a SceneStatusListener with ID = null..");
}
} else {
if (unregisterSceneStatusListeners == null) {
unregisterSceneStatusListeners = new LinkedList<SceneStatusListener>();
}
unregisterSceneStatusListeners.add(sceneStatusListener);
}
}
/**
* Has to be called from a removed Thing-Child to rediscovers the Thing.
*
* @param scene or device id (must not be null)
*/
public void childThingRemoved(String id) {
if (id.split("-").length == 3) {
InternalScene scene = sceneMan.getInternalScene(id);
if (scene != null) {
sceneMan.removeInternalScene(id);
sceneMan.addInternalScene(scene);
}
} else {
devStatMan.removeDevice(id);
}
}
/**
* Delegate a stop command from a Thing to the {@link DeviceStatusManager#sendStopComandsToDSS(Device)}.
*
* @param device
*/
public void stopOutputValue(Device device) {
this.devStatMan.sendStopComandsToDSS(device);
}
@Override
public void channelLinked(ChannelUID channelUID) {
switch (channelUID.getId()) {
case CHANNEL_ID_TOTAL_ACTIVE_POWER:
if (devStatMan != null) {
onTotalPowerConsumptionChanged(devStatMan.getTotalPowerConsumption());
}
break;
case CHANNEL_ID_TOTAL_ELECTRIC_METER:
if (devStatMan != null) {
onEnergyMeterValueChanged(devStatMan.getTotalEnergyMeterValue());
}
}
}
@Override
public void onTotalPowerConsumptionChanged(int newPowerConsumption) {
updateChannelState(CHANNEL_ID_TOTAL_ACTIVE_POWER, newPowerConsumption);
}
@Override
public void onEnergyMeterValueChanged(int newEnergyMeterValue) {
updateChannelState(CHANNEL_ID_TOTAL_ELECTRIC_METER, newEnergyMeterValue * 0.001);
}
private void updateChannelState(String channelID, double value) {
if (getThing().getChannel(channelID) != null) {
updateState(new ChannelUID(getThing().getUID(), channelID), new DecimalType(value));
}
}
@Override
public void onConnectionStateChange(String newConnectionState) {
switch (newConnectionState) {
case CONNECTION_LOST:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"The connection to the digitalSTROM-Server cannot be established.");
devStatMan.stop();
break;
case CONNECTION_RESUMED:
if (connectionTimeoutCounter > 0) {
setStatus(ThingStatus.ONLINE);
devStatMan.start();
}
break;
case APPLICATION_TOKEN_GENERATED:
if (connMan != null) {
Configuration config = this.getConfig();
if (config != null) {
config.remove(USER_NAME);
config.remove(PASSWORD);
config.put(APPLICATION_TOKEN, connMan.getApplicationToken());
this.updateConfiguration(config);
}
}
return;
default:
}
// reset connection timeout counter
connectionTimeoutCounter = 0;
}
private void setStatus(ThingStatus status) {
updateStatus(status);
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
if (handler != null) {
handler.initialize();
}
}
}
@Override
public void onConnectionStateChange(String newConnectionState, String reason) {
if (newConnectionState.equals(NOT_AUTHENTICATED) || newConnectionState.equals(CONNECTION_LOST)) {
switch (reason) {
case WRONG_APP_TOKEN:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"User defined Application-Token is wrong. "
+ "Please set user name and password to generate an Application-Token or set an valide Application-Token.");
break;
case WRONG_USER_OR_PASSWORD:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"The set username or password is wrong.");
break;
case NO_USER_PASSWORD:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No username or password is set to generate Application-Token. Please set user name and password or Application-Token.");
break;
case CONNECTON_TIMEOUT:
// ignore the first connection timeout
if (connectionTimeoutCounter++ > 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Connection lost because connection timeout to Server.");
}
return;
case HOST_NOT_FOUND:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Server not found! Please check these points:\n" + " - Is digitalSTROM-Server turned on?\n"
+ " - Is the host address correct?\n"
+ " - Is the ethernet cable connection established?");
break;
case UNKNOWN_HOST:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Unkown host name, please check the set host name!");
break;
case INVALID_URL:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid URL is set.");
break;
default:
}
// reset connection timeout counter
connectionTimeoutCounter = 0;
if (devStatMan != null) {
devStatMan.stop();
}
}
}
/**
* Returns a list of all {@link Device}'s.
*
* @return device list (cannot be null)
*/
public List<Device> getDevices() {
return this.structMan != null && this.structMan.getDeviceMap() != null
? new LinkedList<Device>(this.structMan.getDeviceMap().values()) : null;
}
/**
* Returns the {@link StructureManager}.
*
* @return StructureManager
*/
public StructureManager getStructureManager() {
return this.structMan;
}
/**
* Delegates a scene command of a Thing to the
* {@link DeviceStatusManager#sendSceneComandsToDSS(InternalScene, boolean)}
*
* @param the called scene
* @param call_undo (true = call scene | false = undo scene)
*/
public void sendSceneComandToDSS(InternalScene scene, boolean call_undo) {
if (devStatMan != null) {
devStatMan.sendSceneComandsToDSS(scene, call_undo);
}
}
/**
* Delegates a device command of a Thing to the
* {@link DeviceStatusManager#sendComandsToDSS(Device, DeviceStateUpdate)}
*
* @param device
* @param deviceStateUpdate
*/
public void sendComandsToDSS(Device device, DeviceStateUpdate deviceStateUpdate) {
if (devStatMan != null) {
devStatMan.sendComandsToDSS(device, deviceStateUpdate);
}
}
/**
* Returns a list of all {@link InternalScene}'s.
*
* @return Scene list (cannot be null)
*/
public List<InternalScene> getScenes() {
return sceneMan != null ? sceneMan.getScenes() : new LinkedList<InternalScene>();
}
/**
* Returns the {@link ConnectionManager}.
*
* @return ConnectionManager
*/
public ConnectionManager getConnectionManager() {
return this.connMan;
}
@Override
public void onStatusChanged(ManagerTypes managerType, ManagerStates state) {
if (managerType.equals(ManagerTypes.DEVICE_STATUS_MANAGER)) {
switch (state) {
case INITIALIZING:
logger.info("Building digitalSTROM model");
break;
case RUNNING:
if (devListener != null) {
for (DeviceStatusListener deviceListener : devListener) {
devStatMan.registerDeviceListener(deviceListener);
}
}
setStatus(ThingStatus.ONLINE);
break;
case STOPPED:
if (!getThing().getStatusInfo().getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)
&& !getThing().getStatusInfo().getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "DeviceStatusManager is stopped.");
}
break;
default:
break;
}
}
if (managerType.equals(ManagerTypes.SCENE_MANAGER)) {
switch (state) {
case GENERATING_SCENES:
generatingScenes = true;
break;
case RUNNING:
if (unregisterSceneStatusListeners != null) {
for (SceneStatusListener sceneListener : this.unregisterSceneStatusListeners) {
sceneMan.registerSceneListener(sceneListener);
}
}
if (sceneListener != null) {
for (SceneStatusListener sceneListener : this.sceneListener) {
sceneMan.registerSceneListener(sceneListener);
}
}
generatingScenes = false;
break;
default:
break;
}
}
}
}