org.openhab.binding.digitalstrom.internal.DigitalSTROMBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.digitalstrom.internal.DigitalSTROMBinding.java

Source

/**
 * Copyright (c) 2010-2015, openHAB.org and others.
 *
 * 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.openhab.binding.digitalstrom.internal;

import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.openhab.binding.digitalstrom.DigitalSTROMBindingProvider;
import org.openhab.binding.digitalstrom.internal.client.DigitalSTROMAPI;
import org.openhab.binding.digitalstrom.internal.client.connection.JSONResponseHandler;
import org.openhab.binding.digitalstrom.internal.client.connection.transport.HttpTransport;
import org.openhab.binding.digitalstrom.internal.client.constants.ApartmentSceneEnum;
import org.openhab.binding.digitalstrom.internal.client.constants.DeviceConstants;
import org.openhab.binding.digitalstrom.internal.client.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.client.constants.JSONRequestConstants;
import org.openhab.binding.digitalstrom.internal.client.constants.MeteringTypeEnum;
import org.openhab.binding.digitalstrom.internal.client.constants.MeteringUnitsEnum;
import org.openhab.binding.digitalstrom.internal.client.constants.OutputModeEnum;
import org.openhab.binding.digitalstrom.internal.client.constants.SceneToStateMapper;
import org.openhab.binding.digitalstrom.internal.client.constants.SensorIndexEnum;
import org.openhab.binding.digitalstrom.internal.client.constants.ZoneSceneEnum;
import org.openhab.binding.digitalstrom.internal.client.entity.Apartment;
import org.openhab.binding.digitalstrom.internal.client.entity.CachedMeteringValue;
import org.openhab.binding.digitalstrom.internal.client.entity.DSID;
import org.openhab.binding.digitalstrom.internal.client.entity.DetailedGroupInfo;
import org.openhab.binding.digitalstrom.internal.client.entity.Device;
import org.openhab.binding.digitalstrom.internal.client.entity.DeviceSceneSpec;
import org.openhab.binding.digitalstrom.internal.client.entity.Event;
import org.openhab.binding.digitalstrom.internal.client.entity.EventItem;
import org.openhab.binding.digitalstrom.internal.client.entity.Zone;
import org.openhab.binding.digitalstrom.internal.client.entity.impl.JSONEventImpl;
import org.openhab.binding.digitalstrom.internal.client.events.DeviceListener;
import org.openhab.binding.digitalstrom.internal.client.events.EventPropertyEnum;
import org.openhab.binding.digitalstrom.internal.client.impl.DigitalSTROMJSONImpl;
import org.openhab.binding.digitalstrom.internal.client.job.DeviceConsumptionSensorJob;
import org.openhab.binding.digitalstrom.internal.client.job.DeviceOutputValueSensorJob;
import org.openhab.binding.digitalstrom.internal.client.job.DeviceSensorValueJob;
import org.openhab.binding.digitalstrom.internal.client.job.SceneOutputValueSensorJob;
import org.openhab.binding.digitalstrom.internal.client.job.SensorJob;
import org.openhab.binding.digitalstrom.internal.config.ConnectionConfig;
import org.openhab.binding.digitalstrom.internal.config.ConsumptionConfig;
import org.openhab.binding.digitalstrom.internal.config.ContextConfig;
import org.openhab.binding.digitalstrom.internal.config.DigitalSTROMBindingConfig;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.DimmerItem;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.RollershutterItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Alexander Betker
 * @author Alex Maier
 * @since 1.3.0
 */
public class DigitalSTROMBinding extends AbstractActiveBinding<DigitalSTROMBindingProvider>
        implements ManagedService, DeviceListener {

    private static final Logger logger = LoggerFactory.getLogger(DigitalSTROMBinding.class);

    /**
     * the interval to find new refresh candidates (defaults to 1000
     * milliseconds)
     */
    private int granularity = 1000;

    /** host name with port of digitalSTROM Server */
    private String uri;

    /** ApplicationToken to connect to digitalSTROM Server */
    private static String applicationToken;

    private int connectTimeout = ConnectionConfig.DEFAULT_CONNECT_TIMEOUT;

    private int readTimeout = ConnectionConfig.DEFAULT_READ_TIMEOUT;

    private String user = null;

    private String password = null;

    private String sessionToken = null;

    private boolean serverIsFound = false;

    private DigitalSTROMAPI digitalSTROM = null;

    private DigitalSTROMEventListener digitalSTROMEventListener = null;

    /** Mapping digitalSTROM-Scene to digitalSTROM-State */
    private SceneToStateMapper stateMapper = new SceneToStateMapper();

    // ######### MAPS ###########

    // openHABItemName - DigitalSTROMDevice
    private Map<String, Device> deviceMap = Collections.synchronizedMap(new HashMap<String, Device>());

    // dsid-String - DigitalSTROMDevice
    private Map<String, Device> dsidToDeviceMap = Collections.synchronizedMap(new HashMap<String, Device>());

    // all digitalSTROM Devices retrieved by digitalSTROM Server
    private Map<String, Device> rawDsidToDeviceMap = Collections.synchronizedMap(new HashMap<String, Device>());

    // zoneID - Map < groupID, List<dsid-String>>
    private Map<Integer, Map<Short, List<String>>> digitalSTROMZoneGroupMap = Collections
            .synchronizedMap(new HashMap<Integer, Map<Short, List<String>>>());

    // itemName - time stamp
    private Map<String, Long> lastUpdateMap = new HashMap<String, Long>();

    // ####### LISTs #########

    private List<String> echoBox = Collections.synchronizedList(new LinkedList<String>());

    private List<SensorJob> highPrioritySensorJobs = Collections.synchronizedList(new LinkedList<SensorJob>());
    private List<SensorJob> mediumPrioritySensorJobs = Collections.synchronizedList(new LinkedList<SensorJob>());
    private List<SensorJob> lowPrioritySensorJobs = Collections.synchronizedList(new LinkedList<SensorJob>());

    private SensorJobExecutor sensorJobExecutor = null;

    public DigitalSTROMBinding() {
    }

    @Override
    public void activate() {
    }

    @Override
    public void deactivate() {
        for (DigitalSTROMBindingProvider provider : providers) {
            provider.removeBindingChangeListener(this);
        }

        if (digitalSTROMEventListener != null) {
            digitalSTROMEventListener.shutdown();
            digitalSTROMEventListener = null;
        }

        if (sensorJobExecutor != null) {
            sensorJobExecutor.shutdown();
            sensorJobExecutor = null;
        }

        removeAllDeviceListener();
        deallocateResources();
        providers.clear();
        digitalSTROM.logout();
    }

    private void removeAllDeviceListener() {
        Map<String, Device> clonedMap = getDsidToDeviceMap();
        Collection<Device> collection = clonedMap.values();

        for (Device device : collection) {
            device.removeDeviceListener(this);
        }
    }

    private void deallocateResources() {
        deviceMap.clear();
        dsidToDeviceMap.clear();
        rawDsidToDeviceMap.clear();
        digitalSTROMZoneGroupMap.clear();
        lastUpdateMap.clear();
        echoBox.clear();
    }

    @Override
    public void setEventPublisher(EventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Override
    public void unsetEventPublisher(EventPublisher eventPublisher) {
        this.eventPublisher = null;
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected long getRefreshInterval() {
        return granularity;
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected String getName() {
        return "digitalstrom Refresh Service";
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected void execute() {
        if (!serverIsFound()) {
            login();
        } else {
            if (digitalSTROM.getTime(getSessionToken()) == -1) {
                logger.warn("test method failed ... new login now");
                login();
            }
        }

        for (DigitalSTROMBindingProvider provider : providers) {
            for (DigitalSTROMBindingConfig itemConf : provider.getAllCircuitConsumptionItems()) {

                String itemName = itemConf.itemName;
                int refreshInterval = itemConf.timeinterval;

                Long lastUpdateTimeStamp = lastUpdateMap.get(itemName);
                if (lastUpdateTimeStamp == null) {
                    lastUpdateTimeStamp = 0L;
                }

                long age = System.currentTimeMillis() - lastUpdateTimeStamp;
                boolean needsUpdate = age >= refreshInterval;

                if (needsUpdate) {

                    logger.debug("item '{}' is about to be refreshed now", itemName);

                    int consumptionValue = -1;

                    if (itemConf.consumption == null
                            || itemConf.consumption.equals(ConsumptionConfig.OUTPUT_CURRENT))
                        itemConf.consumption = ConsumptionConfig.ACTIVE_POWER;

                    switch (itemConf.consumption) {

                    case ACTIVE_POWER:
                        List<CachedMeteringValue> consumptionList = digitalSTROM.getLatest(getSessionToken(),
                                MeteringTypeEnum.consumption,
                                ".meters(" + itemConf.dsmid.getValue().toLowerCase() + ")", null);
                        if (consumptionList != null) {
                            consumptionValue = 0;
                            for (CachedMeteringValue value : consumptionList) {
                                consumptionValue += value.getValue();
                            }
                        }
                        break;
                    case ELECTRIC_METER:
                        List<CachedMeteringValue> energyList = digitalSTROM.getLatest(getSessionToken(),
                                MeteringTypeEnum.energy, ".meters(" + itemConf.dsmid.getValue().toLowerCase() + ")",
                                MeteringUnitsEnum.Wh);
                        if (energyList != null) {
                            consumptionValue = 0;
                            for (CachedMeteringValue value : energyList) {
                                consumptionValue += value.getValue();
                            }
                        }
                        break;
                    default:
                        break;
                    }

                    org.openhab.core.types.State state = UnDefType.NULL;

                    if (consumptionValue != -1) {
                        state = new DecimalType(consumptionValue);
                    }

                    if (state != null) {
                        eventPublisher.postUpdate(itemName, state);
                    }
                    lastUpdateMap.put(itemName, System.currentTimeMillis());
                }
            }

            for (DigitalSTROMBindingConfig itemConf : provider.getAllDeviceConsumptionItems()) {

                String itemName = itemConf.itemName;
                int refreshInterval = itemConf.timeinterval;

                Long lastUpdateTimeStamp = lastUpdateMap.get(itemName);
                if (lastUpdateTimeStamp == null) {
                    lastUpdateTimeStamp = 0L;
                }

                long age = System.currentTimeMillis() - lastUpdateTimeStamp;
                boolean needsUpdate = age >= refreshInterval;

                if (needsUpdate) {
                    logger.debug("item '{}' is about to be refreshed now", itemName);

                    Device device = getDsidToDeviceMap().get(itemConf.dsid.getValue());
                    if (device != null) {

                        if (itemConf.sensor == null) {
                            SensorIndexEnum sensorIndex = null;
                            try {
                                sensorIndex = SensorIndexEnum.valueOf(itemConf.consumption.name());
                            } catch (Exception e) {
                                sensorIndex = SensorIndexEnum.ACTIVE_POWER;
                            }
                            addLowPriorityJob(new DeviceConsumptionSensorJob(device, sensorIndex));
                            lastUpdateMap.put(itemName, System.currentTimeMillis());
                        } else {
                            SensorIndexEnum sensorIndex = null;
                            try {
                                sensorIndex = SensorIndexEnum.valueOf(itemConf.sensor.name());
                            } catch (Exception e) {

                            }
                            addHighPriorityJob(new DeviceSensorValueJob(device, sensorIndex));
                        }

                    }
                }
            }

        }
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected void internalReceiveCommand(String itemName, Command command) {
        logger.debug("internalReceiveCommand() is called for item: " + itemName);
        deviceCall(itemName, command);
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected void internalReceiveUpdate(String itemName, State newState) {
        // the code being executed when a state was sent on the openHAB
        // event bus goes here. This method is only called if one of the
        // BindingProviders provide a binding for the given 'itemName'.
        logger.debug("internalReceiveUpdate() is called for item: " + itemName);
        eventPublisher.postUpdate(itemName, newState);
    }

    /**
     * @{inheritDoc
     */
    @Override
    public void updated(Dictionary<String, ?> config) throws ConfigurationException {
        if (config != null) {

            String refreshIntervalStr = (String) config.get("refreshinterval");
            if (StringUtils.isNotBlank(refreshIntervalStr)) {
                granularity = Integer.parseInt(refreshIntervalStr);
            }

            String uriStr = (String) config.get("uri");
            if (StringUtils.isNotBlank(uriStr)) {
                uri = uriStr;
            }

            String connectTimeoutStr = (String) config.get("connectTimeout");
            if (StringUtils.isNotBlank(connectTimeoutStr)) {
                connectTimeout = Integer.parseInt(connectTimeoutStr);
            }

            String readTimeoutStr = (String) config.get("readTimeout");
            if (StringUtils.isNotBlank(readTimeoutStr)) {
                readTimeout = Integer.parseInt(readTimeoutStr);
            }

            String applicationTokenStr = (String) config.get("loginToken");
            if (StringUtils.isNotBlank(applicationTokenStr)) {
                applicationToken = applicationTokenStr;
            }

            String userStr = (String) config.get("user");
            if (StringUtils.isNotBlank(userStr)) {
                user = userStr;
            }

            String passwordStr = (String) config.get("password");
            if (StringUtils.isNotBlank(passwordStr)) {
                password = passwordStr;
            }
            this.digitalSTROM = new DigitalSTROMJSONImpl(uri, connectTimeout, readTimeout);

            registerDigitalSTROMEventListener();
            startSensorJobExecutor();
            initializeDevices();

            setProperlyConfigured(true);
        }
    }

    private void initializeDevices() {
        for (DigitalSTROMBindingProvider provider : this.providers) {

            Collection<String> itemNames = provider.getItemNames();

            //initialize devices
            for (String itemName : itemNames) {
                DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
                if (confItem != null && confItem.dsid != null) {
                    if (rawDsidToDeviceMap.size() == 0 && serverIsFound()) {
                        rawDsidToDeviceMap = getAllDigitalSTROMDevicesMap();
                    }
                    Device device = rawDsidToDeviceMap.get(confItem.dsid.getValue());
                    if (device != null) {
                        addDevice(itemName, device);
                        updateItemState(confItem.item);
                        handleStructure(digitalSTROM.getApartmentStructure(getSessionToken()));
                    }
                }
            }
        }
    }

    private Map<String, Device> getDsidToDeviceMap() {
        return new HashMap<String, Device>(dsidToDeviceMap);
    }

    private Map<Integer, Map<Short, List<String>>> getDigitalSTROMZoneGroupMap() {
        return new HashMap<Integer, Map<Short, List<String>>>(digitalSTROMZoneGroupMap);
    }

    private Map<String, Device> getAllDigitalSTROMDevicesMap() {
        Map<String, Device> deviceMap = new HashMap<String, Device>();

        for (Device device : digitalSTROM.getApartmentDevices(getSessionToken(), false)) {
            deviceMap.put(device.getDSID().getValue(), device);
        }
        return deviceMap;
    }

    private void addDevice(String itemName, Device dev) {
        synchronized (deviceMap) {
            if (!deviceMap.containsKey(itemName)) {
                deviceMap.put(itemName, dev);
                dev.addDeviceListener(this);
            } else {
                logger.warn("device already exists in deviceMap: " + itemName);
            }
        }
        synchronized (dsidToDeviceMap) {
            if (!dsidToDeviceMap.containsKey(dev.getDSID().getValue())) {
                dsidToDeviceMap.put(dev.getDSID().getValue(), dev);
            }
        }
    }

    // Here we build up a new hashmap in order to replace it with the old one.
    // This hashmap is used to find the affected items after an event from
    // digitalSTROM.
    private void handleStructure(Apartment apartment) {
        if (apartment != null) {

            Map<Integer, Map<Short, List<String>>> newZoneGroupMap = Collections
                    .synchronizedMap(new HashMap<Integer, Map<Short, List<String>>>());
            Map<String, Device> clonedDsidMap = getDsidToDeviceMap();

            for (Zone zone : apartment.getZoneMap().values()) {

                Map<Short, List<String>> groupMap = new HashMap<Short, List<String>>();

                for (DetailedGroupInfo g : zone.getGroups()) {

                    List<String> devicesInGroup = new LinkedList<String>();
                    for (String dsid : g.getDeviceList()) {
                        if (clonedDsidMap.containsKey(dsid)) {
                            devicesInGroup.add(dsid);
                        }
                    }
                    groupMap.put(g.getGroupID(), devicesInGroup);
                }
                newZoneGroupMap.put(zone.getZoneId(), groupMap);
            }

            synchronized (digitalSTROMZoneGroupMap) {
                digitalSTROMZoneGroupMap = newZoneGroupMap;
            }
        }
    }

    private DigitalSTROMBindingConfig getConfigForItemName(String itemName) {
        for (DigitalSTROMBindingProvider provider : this.providers) {
            if (provider.getItemConfig(itemName) != null) {
                return provider.getItemConfig(itemName);
            }
        }
        return null;
    }

    private List<String> getItemNamesForDsid(String dsid) {
        for (DigitalSTROMBindingProvider provider : this.providers) {
            if (provider.getItemNamesByDsid(dsid) != null) {
                return provider.getItemNamesByDsid(dsid);
            }
        }
        return null;
    }

    private void deviceCall(String itemName, Command cm) {
        Device device = deviceMap.get(itemName);

        if (device != null) {
            if (cm instanceof org.openhab.core.library.types.OnOffType) {

                if (((org.openhab.core.library.types.OnOffType) cm).equals(OnOffType.ON)) {

                    boolean transmitted = digitalSTROM.turnDeviceOn(getSessionToken(), device.getDSID(), null);
                    if (transmitted) {
                        device.setOutputValue(device.getMaxOutPutValue());
                        addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.MAXIMUM.getSceneNumber());
                    }

                } else if (((org.openhab.core.library.types.OnOffType) cm).equals(OnOffType.OFF)) {

                    boolean transmitted = digitalSTROM.turnDeviceOff(getSessionToken(), device.getDSID(), null);
                    if (transmitted) {
                        device.setOutputValue(0);
                        addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.MINIMUM.getSceneNumber());
                    }

                }

            } else if (cm instanceof org.openhab.core.library.types.IncreaseDecreaseType) {

                if (!device.isDimmable()) {
                    logger.warn("device is not in dimm mode: " + itemName + " outputMode: "
                            + device.getOutputMode().getMode());
                    return;
                }

                if (((org.openhab.core.library.types.IncreaseDecreaseType) cm)
                        .equals(IncreaseDecreaseType.INCREASE)) {

                    boolean transmitted = digitalSTROM.callDeviceScene(getSessionToken(), device.getDSID(), null,
                            ZoneSceneEnum.INCREMENT, false);
                    if (transmitted) {
                        addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.INCREMENT.getSceneNumber());

                        if (device.getOutputValue() == 0) {
                            initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
                        } else {
                            device.increase();
                        }
                    } else {
                        logger.error("transmitting increase command FAILED " + itemName);
                    }

                } else if (((org.openhab.core.library.types.IncreaseDecreaseType) cm)
                        .equals(IncreaseDecreaseType.DECREASE)) {

                    boolean transmitted = digitalSTROM.callDeviceScene(getSessionToken(), device.getDSID(), null,
                            ZoneSceneEnum.DECREMENT, false);
                    if (transmitted) {
                        addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.DECREMENT.getSceneNumber());
                        device.decrease();
                    } else {
                        logger.error("transmitting decrease command FAILED " + itemName);
                    }
                }

            } else if (cm instanceof org.openhab.core.library.types.PercentType) {
                int percent = -1;
                try {
                    percent = (int) Float.parseFloat(cm.toString());
                } catch (java.lang.NumberFormatException e) {
                    logger.error("NumberFormatException on a PercentType with command: " + cm.toString());
                }
                if (percent != -1) {
                    if (percent > -1 && percent < 101) {

                        if (device.getOutputMode().equals(OutputModeEnum.SLAT)) {

                            DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
                            if (confItem != null) {

                                if (confItem.context != null && confItem.context.equals(ContextConfig.slat)) {

                                    int old = device.getSlatPosition();
                                    device.setSlatPosition(
                                            fromPercentToValue(percent, device.getMaxSlatPosition()));

                                    boolean transmitted = digitalSTROM.setDeviceOutputValue(getSessionToken(),
                                            device.getDSID(), null, DeviceConstants.DEVICE_SENSOR_SLAT_OUTPUT,
                                            fromPercentToValue(percent, device.getMaxSlatPosition()));
                                    if (!transmitted) {
                                        device.setSlatPosition(old);
                                        logger.error("could NOT successfully set new value for slats ..."
                                                + cm.toString());
                                    }
                                } else {

                                    int old = device.getOutputValue();

                                    device.setOutputValue(fromPercentToValue(percent, device.getMaxOutPutValue()));

                                    boolean transmitted = digitalSTROM.setDeviceValue(getSessionToken(),
                                            device.getDSID(), null,
                                            fromPercentToValue(percent, device.getMaxOutPutValue()));
                                    if (!transmitted) {
                                        device.setOutputValue(old);
                                        logger.error("could NOT successfully set new value ..." + cm.toString());
                                    }

                                }
                            }

                        } else {

                            int old = device.getOutputValue();

                            device.setOutputValue(fromPercentToValue(percent, device.getMaxOutPutValue()));

                            boolean transmitted = digitalSTROM.setDeviceValue(getSessionToken(), device.getDSID(),
                                    null, fromPercentToValue(percent, device.getMaxOutPutValue()));
                            if (!transmitted) {
                                device.setOutputValue(old);
                                logger.error("could NOT successfully set new value ..." + cm.toString());
                            }

                        }
                    }
                }
            } else if (cm instanceof org.openhab.core.library.types.StopMoveType) {

                if (device.getOutputMode().equals(OutputModeEnum.SLAT)) {

                    DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
                    if (confItem != null) {
                        if (confItem.context != null && confItem.context.equals(ContextConfig.slat)) {
                            logger.warn(
                                    "stop and move command NOT possible for slats, use PercentType command or up and down please");
                        } else {
                            handleStopMoveForRollershutter(device, cm);
                        }
                    }
                } else if (device.getOutputMode().equals(OutputModeEnum.UP_DOWN)) {
                    handleStopMoveForRollershutter(device, cm);
                }
            } else if (cm instanceof org.openhab.core.library.types.UpDownType) {

                if (device.getOutputMode().equals(OutputModeEnum.SLAT)) {

                    // 255 is max open, 0 is closed
                    DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
                    if (confItem != null) {

                        if (confItem.context != null && confItem.context.equals(ContextConfig.slat)) {

                            if (((org.openhab.core.library.types.UpDownType) cm).equals(UpDownType.UP)) {

                                int slatPosition = device.getSlatPosition();
                                int newPosition = slatPosition + DeviceConstants.MOVE_STEP_SLAT;
                                if (newPosition > device.getMaxSlatPosition()) {
                                    newPosition = device.getMaxSlatPosition();
                                }

                                boolean transmitted = digitalSTROM.setDeviceOutputValue(getSessionToken(),
                                        device.getDSID(), null, DeviceConstants.DEVICE_SENSOR_SLAT_OUTPUT,
                                        newPosition);
                                if (transmitted) {
                                    device.setSlatPosition(newPosition);
                                }

                            } else if (((org.openhab.core.library.types.UpDownType) cm).equals(UpDownType.DOWN)) {

                                int slatPosition = device.getSlatPosition();
                                int newPosition = slatPosition - DeviceConstants.MOVE_STEP_SLAT;
                                if (newPosition < device.getMinSlatPosition()) {
                                    newPosition = device.getMinSlatPosition();
                                }
                                boolean transmitted = digitalSTROM.setDeviceOutputValue(getSessionToken(),
                                        device.getDSID(), null, DeviceConstants.DEVICE_SENSOR_SLAT_OUTPUT,
                                        newPosition);

                                if (transmitted) {
                                    device.setSlatPosition(newPosition);
                                }
                            }
                        } else {
                            handleUpDownForRollershutter(device, cm);
                        }
                    }

                } else if (device.getOutputMode().equals(OutputModeEnum.UP_DOWN)) {
                    handleUpDownForRollershutter(device, cm);
                } else {
                    logger.warn("Wrong item configuration ... this hardware is not a rollershutter: " + itemName);
                }
            }

        } else {

            // this item is not mapped to real hardware, it is to make scene
            // calls (as they are known in digitalSTROM)

            if (cm instanceof DecimalType) {
                DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
                if (confItem != null && confItem.context != null) {
                    if (confItem.context.equals(ContextConfig.apartment)) {
                        digitalSTROM.callApartmentScene(getSessionToken(), confItem.groupID, null,
                                ApartmentSceneEnum.getApartmentScene(((DecimalType) cm).intValue()), false);
                    } else if (confItem.context.equals(ContextConfig.zone)) {
                        digitalSTROM.callZoneScene(getSessionToken(), confItem.zoneID, null, confItem.groupID, null,
                                ZoneSceneEnum.getZoneScene(((DecimalType) cm).intValue()), false);
                    }
                }
            } else if (cm instanceof StringType) {
                DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
                if (confItem != null && confItem.context != null) {
                    int scene = -1;
                    try {
                        scene = Integer.parseInt(cm.toString());
                    } catch (java.lang.NumberFormatException e) {
                        logger.error(
                                "NumberFormatException by parsing " + cm.toString() + " for " + confItem.itemName);
                    }
                    if (scene != -1) {
                        if (confItem.context.equals(ContextConfig.apartment)) {
                            digitalSTROM.callApartmentScene(getSessionToken(), confItem.groupID, null,
                                    ApartmentSceneEnum.getApartmentScene(scene), false);
                        } else if (confItem.context.equals(ContextConfig.zone)) {
                            digitalSTROM.callZoneScene(getSessionToken(), confItem.zoneID, null, confItem.groupID,
                                    null, ZoneSceneEnum.getZoneScene(scene), false);
                        }
                    }
                }
            } else {
                logger.warn("couldn't find digitalstrom device for " + itemName);
            }
        }
    }

    private void handleUpDownForRollershutter(Device device, Command cm) {
        if (((org.openhab.core.library.types.UpDownType) cm).equals(UpDownType.UP)) {

            boolean transmitted = digitalSTROM.callDeviceScene(getSessionToken(), device.getDSID(), null,
                    ZoneSceneEnum.MAXIMUM, false);
            if (transmitted) {
                addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.MAXIMUM.getSceneNumber());
            } else {
                logger.error("could not transmit command UP for dsid: " + device.getDSID().getValue());
            }

        } else if (((org.openhab.core.library.types.UpDownType) cm).equals(UpDownType.DOWN)) {

            boolean transmitted = digitalSTROM.callDeviceScene(getSessionToken(), device.getDSID(), null,
                    ZoneSceneEnum.MINIMUM, false);

            if (transmitted) {
                addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.MINIMUM.getSceneNumber());
            } else {
                logger.error("could not transmit command DOWN for dsid: " + device.getDSID().getValue());
            }
        }
    }

    private void handleStopMoveForRollershutter(Device device, Command cm) {
        if (((org.openhab.core.library.types.StopMoveType) cm).equals(StopMoveType.MOVE)) {

            boolean transmitted = digitalSTROM.callDeviceScene(getSessionToken(), device.getDSID(), null,
                    ZoneSceneEnum.AREA_STEPPING_CONTINUE, false);

            if (transmitted) {
                addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.AREA_STEPPING_CONTINUE.getSceneNumber());
            } else {
                logger.error("could NOT transmit command MOVE for dsid: " + device.getDSID().getValue());
            }

        } else if (((org.openhab.core.library.types.StopMoveType) cm).equals(StopMoveType.STOP)) {

            boolean transmitted = digitalSTROM.callDeviceScene(getSessionToken(), device.getDSID(), null,
                    ZoneSceneEnum.STOP, false);

            if (transmitted) {
                addEcho(device.getDSID().getValue(), (short) ZoneSceneEnum.STOP.getSceneNumber());

                initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
            } else {
                logger.warn("could NOT transmit command STOP for dsid: " + device.getDSID().getValue());
            }
        }
    }

    private int fromPercentToValue(int percent, int max) {
        if (percent < 0 || percent == 0) {
            return 0;
        }
        if (max < 0 || max == 0) {
            return 0;
        }
        return (int) (max * (float) ((float) percent / 100));
    }

    // ... we want to ignore own 'command-echos'
    private void addEcho(String dsid, short sceneId) {
        synchronized (echoBox) {
            echoBox.add(dsid + "-" + sceneId);
        }
    }

    private boolean isEcho(String dsid, short sceneId) {
        String echo = dsid + "-" + sceneId;
        synchronized (echoBox) {
            if (echoBox.contains(echo)) {
                echoBox.remove(echo);
                return true;
            }
        }
        return false;
    }

    private boolean isApartmentScene(short sceneId) {
        return (sceneId > 63);
    }

    private boolean isDimmScene(short sceneId) {
        if (sceneId > 9 && sceneId < 13) {
            return true;
        }
        if (sceneId == 15) { // command to dim or for a roller shutter
            return true;
        }
        if (sceneId > 41 && sceneId < 50) {
            return true;
        }
        if (sceneId > 51 && sceneId < 56) { // command to dim or for a roller
            // shutter
            return true;
        }
        return false;
    }

    private void handleDimmScene(Device device, short sceneID, short groupID, boolean force) {

        if ((groupID == -1 && !force)) {
            return;
        }

        if (device.isDimmable()) {

            switch (sceneID) {

            case 11:
            case 42:
            case 44:
            case 46:
            case 48:
                decrease(device);
                break;

            case 12:
            case 43:
            case 45:
            case 47:
            case 49:
                increase(device);
                break;
            default:
                break;

            }

        } else if (device.isRollershutter()) {
            switch (sceneID) {

            case 15:

                initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
                if (device.getOutputMode().equals(OutputModeEnum.SLAT)) {
                    initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_SLAT_OUTPUT);
                }
                break;
            case 52:
            case 53:
            case 54:
            case 55:

                initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
                if (device.getOutputMode().equals(OutputModeEnum.SLAT)) {
                    initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_SLAT_OUTPUT);
                }
                break;
            default:

            }
        }

    }

    private void decrease(Device device) {
        initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
    }

    private void increase(Device device) {
        initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
    }

    private void handleApartmentScene(short sceneId, short groupId) {

        if (groupId == 0) {
            Map<String, Device> clonedDeviceMap = getDsidToDeviceMap();
            Set<String> dsidSet = clonedDeviceMap.keySet();

            for (String dsid : dsidSet) {
                Device device = clonedDeviceMap.get(dsid);

                if (device != null) {

                    if (!device.containsSceneConfig(sceneId)) {
                        getSceneSpec(device, sceneId);
                    }

                    if (!device.doIgnoreScene(sceneId)) {
                        short output = device.getSceneOutputValue(sceneId);
                        if (output != -1) {
                            device.setOutputValue(output);
                        } else {
                            initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
                            initSceneOutputValue(device, sceneId);
                        }
                    }
                }
            }
        } else if (groupId != -1) {

            Map<String, Device> clonedDeviceMap = getDsidToDeviceMap();
            Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(0);
            List<String> dsidList = map.get(groupId);

            if (dsidList != null) {
                for (String dsid : dsidList) {
                    Device device = clonedDeviceMap.get(dsid);

                    if (device != null) {

                        if (!device.containsSceneConfig(sceneId)) {
                            getSceneSpec(device, sceneId);
                        }

                        if (!device.doIgnoreScene(sceneId)) {
                            short output = device.getSceneOutputValue(sceneId);
                            if (output != -1) {
                                device.setOutputValue(output);
                            } else {
                                initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
                                initSceneOutputValue(device, sceneId);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * only works on openhabEvent! please copy "openhab/openhab.js" to your dSS
     * server (/usr/share/dss/add-ons/) and "openhab.xml" to
     * /usr/share/dss/data/subscriptions.d/ than you need to restart your dSS
     * 
     * If you don't, you will not get detailed infos about, what exactly
     * happened (for example: which device was turned on by a browser or handy
     * app )
     * 
     * @param eventItem
     */
    private void handleOpenhabEvent(EventItem eventItem) {
        if (eventItem != null) {
            int zoneId = -1;
            short groupId = -1;
            short sceneId = -1;

            boolean isDeviceCall = false;
            String dsidStr = null;

            String zoneIDStr = eventItem.getProperties().get(EventPropertyEnum.ZONEID);
            if (zoneIDStr != null) {
                try {
                    zoneId = Integer.parseInt(zoneIDStr);
                } catch (java.lang.NumberFormatException e) {
                    logger.error("NumberFormatException by handling event at parsing zoneId");
                }
            }

            String sceneStr = eventItem.getProperties().get(EventPropertyEnum.SCENEID);
            if (sceneStr != null) {
                try {
                    sceneId = Short.parseShort(sceneStr);
                } catch (java.lang.NumberFormatException e) {
                    logger.error("NumberFormatException by handling event at parsing sceneId: " + sceneStr);
                }
            }

            String groupStr = eventItem.getProperties().get(EventPropertyEnum.GROUPID);
            if (groupStr != null) {
                try {
                    groupId = Short.parseShort(groupStr);
                } catch (java.lang.NumberFormatException e) {
                    logger.error("NumberFormatException by handling event at parsing groupId");
                }
            }

            dsidStr = eventItem.getProperties().get(EventPropertyEnum.DSID);

            String deviceCallStr = eventItem.getProperties().get(EventPropertyEnum.IS_DEVICE_CALL);
            if (deviceCallStr != null) {
                isDeviceCall = deviceCallStr.equals("true");
            }

            if (sceneId != -1) {

                if (!isEcho(dsidStr, sceneId)) {

                    if (isDeviceCall) {

                        if (dsidStr != null) {
                            Device device = getDsidToDeviceMap().get(dsidStr);

                            if (device != null) {
                                if (!device.containsSceneConfig(sceneId)) {
                                    getSceneSpec(device, sceneId);
                                }

                                if (isDimmScene(sceneId)) {
                                    if (!device.doIgnoreScene(sceneId)) {
                                        handleDimmScene(device, sceneId, (short) -1, true);
                                    }
                                } else if (stateMapper.isMappable(sceneId)) {
                                    boolean shouldBeOn = stateMapper.getMapping(sceneId);

                                    if (!device.doIgnoreScene(sceneId)) {
                                        if (shouldBeOn) {
                                            device.setOutputValue(device.getMaxOutPutValue());
                                        } else {
                                            device.setOutputValue(0);
                                        }
                                    }
                                } else {
                                    if (!device.doIgnoreScene(sceneId)) {
                                        short value = device.getSceneOutputValue(sceneId);
                                        if (value != -1) {
                                            device.setOutputValue(value);
                                        } else {
                                            initDeviceOutputValue(device, DeviceConstants.DEVICE_SENSOR_OUTPUT);
                                            initSceneOutputValue(device, sceneId);
                                        }
                                    }
                                }
                            }
                        }
                    } else {

                        if (isApartmentScene(sceneId)) {
                            handleApartmentScene(sceneId, groupId);
                        } else {

                            if (zoneId == 0) {
                                if (isDimmScene(sceneId)) {

                                    Map<String, Device> deviceMap = getDsidToDeviceMap();

                                    if (groupId == 0) {

                                        Set<String> dsidSet = deviceMap.keySet();

                                        if (dsidSet != null) {
                                            for (String dsid : dsidSet) {
                                                Device device = deviceMap.get(dsid);

                                                if (device != null) {

                                                    if (!device.containsSceneConfig(sceneId)) {
                                                        getSceneSpec(device, sceneId);
                                                    }

                                                    if (!device.doIgnoreScene(sceneId)) {
                                                        handleDimmScene(deviceMap.get(dsid), sceneId, groupId,
                                                                false);
                                                    }

                                                }
                                            }
                                        }
                                    } else if (groupId != -1) {

                                        Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(zoneId);

                                        if (map != null) {
                                            List<String> dsidList = map.get(groupId);
                                            if (dsidList != null) {
                                                for (String dsid : dsidList) {
                                                    Device device = deviceMap.get(dsid);

                                                    if (device != null) {

                                                        if (!device.containsSceneConfig(sceneId)) {
                                                            getSceneSpec(device, sceneId);
                                                        }

                                                        if (!device.doIgnoreScene(sceneId)) {
                                                            handleDimmScene(deviceMap.get(dsid), sceneId, groupId,
                                                                    false);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else if (stateMapper.isMappable(sceneId)) {

                                    boolean shouldBeOn = stateMapper.getMapping(sceneId);

                                    if (groupId == 0) {

                                        Map<String, Device> deviceMap = getDsidToDeviceMap();
                                        Set<String> dsidSet = deviceMap.keySet();
                                        if (dsidSet != null) {
                                            for (String dsid : dsidSet) {
                                                Device device = deviceMap.get(dsid);
                                                if (device != null) {

                                                    if (!device.containsSceneConfig(sceneId)) {
                                                        getSceneSpec(device, sceneId);
                                                    }

                                                    if (!device.doIgnoreScene(sceneId)) {

                                                        if (shouldBeOn) {
                                                            device.setOutputValue(device.getMaxOutPutValue());
                                                        } else {
                                                            device.setOutputValue(0);
                                                        }

                                                    }
                                                }
                                            }
                                        }
                                    } else if (groupId != -1) {

                                        Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(zoneId);
                                        Map<String, Device> deviceMap = getDsidToDeviceMap();
                                        if (map != null) {
                                            List<String> dsidList = map.get(groupId);
                                            if (dsidList != null) {
                                                for (String dsid : dsidList) {
                                                    Device device = deviceMap.get(dsid);
                                                    if (device != null) {

                                                        if (!device.containsSceneConfig(sceneId)) {
                                                            getSceneSpec(device, sceneId);
                                                        }

                                                        if (!device.doIgnoreScene(sceneId)) {

                                                            if (shouldBeOn) {
                                                                device.setOutputValue(device.getMaxOutPutValue());
                                                            } else {
                                                                device.setOutputValue(0);
                                                            }

                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else {

                                    Map<String, Device> deviceMap = getDsidToDeviceMap();

                                    if (groupId != -1) {
                                        Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(zoneId);
                                        if (map != null) {
                                            List<String> dsidList = map.get(groupId);
                                            if (dsidList != null) {
                                                for (String dsid : dsidList) {
                                                    Device device = deviceMap.get(dsid);

                                                    if (device != null) {

                                                        if (!device.containsSceneConfig(sceneId)) {
                                                            getSceneSpec(device, sceneId);
                                                        }

                                                        if (!device.doIgnoreScene(sceneId)) {
                                                            short sceneValue = device.getSceneOutputValue(sceneId);
                                                            if (sceneValue == -1) {
                                                                initDeviceOutputValue(device,
                                                                        DeviceConstants.DEVICE_SENSOR_OUTPUT);
                                                                initSceneOutputValue(device, sceneId);
                                                            } else {
                                                                device.setOutputValue(sceneValue);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                            else {

                                if (isDimmScene(sceneId)) {

                                    if (groupId != -1) {
                                        Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(zoneId);
                                        if (map != null) {
                                            List<String> devicesInGroup = map.get(groupId);
                                            if (devicesInGroup != null) {
                                                Map<String, Device> deviceMap = getDsidToDeviceMap();

                                                for (String dsid : devicesInGroup) {
                                                    Device device = deviceMap.get(dsid);

                                                    if (device != null) {

                                                        if (!device.containsSceneConfig(sceneId)) {
                                                            getSceneSpec(device, sceneId);
                                                        }

                                                        if (!device.doIgnoreScene(sceneId)) {
                                                            handleDimmScene(deviceMap.get(dsid), sceneId, groupId,
                                                                    false);
                                                        }

                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else if (stateMapper.isMappable(sceneId)) {

                                    boolean shouldBeOn = stateMapper.getMapping(sceneId);
                                    Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(zoneId);
                                    Map<String, Device> deviceMap = getDsidToDeviceMap();

                                    if (map != null) {

                                        if (groupId != -1) {
                                            List<String> devicesInGroup = map.get(groupId);
                                            if (devicesInGroup != null) {
                                                for (String dsid : devicesInGroup) {
                                                    Device device = deviceMap.get(dsid);

                                                    if (device != null) {
                                                        if (!device.containsSceneConfig(sceneId)) {
                                                            getSceneSpec(device, sceneId);
                                                        }

                                                        if (!device.doIgnoreScene(sceneId)) {
                                                            if (shouldBeOn) {
                                                                device.setOutputValue(device.getMaxOutPutValue());
                                                            } else {
                                                                device.setOutputValue(0);
                                                            }
                                                        }

                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else {

                                    Map<Short, List<String>> map = getDigitalSTROMZoneGroupMap().get(zoneId);
                                    Map<String, Device> deviceMap = getDsidToDeviceMap();
                                    if (map != null) {

                                        if (groupId != -1) {
                                            List<String> devicesInGroup = map.get(groupId);
                                            if (devicesInGroup != null) {
                                                for (String dsid : devicesInGroup) {
                                                    Device device = deviceMap.get(dsid);
                                                    if (device != null) {

                                                        if (!device.containsSceneConfig(sceneId)) {
                                                            getSceneSpec(device, sceneId);
                                                        }

                                                        if (!device.doIgnoreScene(sceneId)) {
                                                            short outputValue = device.getSceneOutputValue(sceneId);
                                                            if (outputValue == -1) {
                                                                initDeviceOutputValue(device,
                                                                        DeviceConstants.DEVICE_SENSOR_OUTPUT);
                                                                initSceneOutputValue(device, sceneId);
                                                            } else {
                                                                device.setOutputValue(outputValue);
                                                            }
                                                        }

                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                logger.error("an event without a sceneID; groupID:" + groupId + ", zoneID:" + zoneId
                        + ", isDeviceCall:" + deviceCallStr + ", dsid:" + dsidStr);
            }
        }
    }

    // Here we read the configured and stored (in the chip) output value for a
    // specific scene
    // and we store this value in order to know next time what to do.
    // The first time a scene command is called, it takes some time for the
    // sensor reading,
    // but the next time we react very fast because we learned what to do on
    // this command.
    private void getSceneSpec(Device device, short sceneId) {

        // setSensorReading(true); // no metering in this time
        DeviceSceneSpec spec = digitalSTROM.getDeviceSceneMode(getSessionToken(), device.getDSID(), null, sceneId);
        // setSensorReading(false);

        if (spec != null) {
            device.addSceneConfig(sceneId, spec);
            logger.info("UPDATED ignoreList for dsid: " + device.getDSID() + " sceneID: " + sceneId);
        }

    }

    private void initDeviceOutputValue(Device device, short index) {
        addHighPriorityJob(new DeviceOutputValueSensorJob(device, index));
    }

    private void initSceneOutputValue(Device device, short sceneId) {
        addMediumPriorityJob(new SceneOutputValueSensorJob(device, sceneId));
    }

    private void addHighPriorityJob(DeviceOutputValueSensorJob deviceOutputValueSensorJob) {
        synchronized (highPrioritySensorJobs) {
            if (!highPrioritySensorJobs.contains(deviceOutputValueSensorJob)) {
                highPrioritySensorJobs.add(deviceOutputValueSensorJob);
            }
        }
    }

    private void addHighPriorityJob(DeviceSensorValueJob deviceSensorValueJob) {
        synchronized (highPrioritySensorJobs) {
            if (!highPrioritySensorJobs.contains(deviceSensorValueJob)) {
                highPrioritySensorJobs.add(deviceSensorValueJob);
            }
        }
    }

    private void addMediumPriorityJob(SceneOutputValueSensorJob sceneOutputValueSensorJob) {
        synchronized (mediumPrioritySensorJobs) {
            if (!mediumPrioritySensorJobs.contains(sceneOutputValueSensorJob)) {
                mediumPrioritySensorJobs.add(sceneOutputValueSensorJob);
            }
        }
    }

    private void addLowPriorityJob(DeviceConsumptionSensorJob deviceConsumptionSensorJob) {
        synchronized (lowPrioritySensorJobs) {
            if (!lowPrioritySensorJobs.contains(deviceConsumptionSensorJob)) {
                lowPrioritySensorJobs.add(deviceConsumptionSensorJob);
            }
        }
    }

    private SensorJob getLowPriorityJob() {
        SensorJob job = null;
        synchronized (lowPrioritySensorJobs) {
            if (lowPrioritySensorJobs.size() > 0) {
                job = lowPrioritySensorJobs.get(0);
                lowPrioritySensorJobs.remove(job);
            }
        }
        return job;
    }

    private SensorJob getMediumPriorityJob() {
        SensorJob job = null;
        synchronized (mediumPrioritySensorJobs) {
            if (mediumPrioritySensorJobs.size() > 0) {
                job = mediumPrioritySensorJobs.get(0);
                mediumPrioritySensorJobs.remove(job);
            }
        }
        return job;
    }

    private SensorJob getHighPriorityJob() {
        SensorJob job = null;
        synchronized (highPrioritySensorJobs) {
            if (highPrioritySensorJobs.size() > 0) {
                job = highPrioritySensorJobs.get(0);
                highPrioritySensorJobs.remove(job);
            }
        }
        return job;
    }

    private void removeSensorJobs(DSID dsid) {
        synchronized (lowPrioritySensorJobs) {
            for (Iterator<SensorJob> iter = lowPrioritySensorJobs.iterator(); iter.hasNext();) {
                SensorJob job = iter.next();
                if (job.getDsid().equals(dsid))
                    iter.remove();
            }
        }
        synchronized (mediumPrioritySensorJobs) {
            for (Iterator<SensorJob> iter = mediumPrioritySensorJobs.iterator(); iter.hasNext();) {
                SensorJob job = iter.next();
                if (job.getDsid().equals(dsid))
                    iter.remove();
            }
        }
        synchronized (highPrioritySensorJobs) {
            for (Iterator<SensorJob> iter = highPrioritySensorJobs.iterator(); iter.hasNext();) {
                SensorJob job = iter.next();
                if (job.getDsid().equals(dsid))
                    iter.remove();
            }
        }
    }

    private void login() {
        if (applicationToken != null) {
            String token = digitalSTROM.loginApplication(applicationToken);

            if (token == null && user != null && password != null) {
                token = digitalSTROM.login(user, password);
            }
            setSessionToken(token);
        } else if (this.user != null && this.password != null) {
            setSessionToken(digitalSTROM.login(user, password));
        }
        if (serverIsFound()) {
            handleStructure(digitalSTROM.getApartmentStructure(getSessionToken()));
        }
    }

    private void setSessionToken(String newToken) {
        sessionToken = newToken;
        if (newToken != null) {
            setServerIsFound(true);
            logger.info("SUCCESSFULLY got session-token");
        } else {
            setServerIsFound(false);
            logger.error("could NOT get session-token");
        }
    }

    private String getSessionToken() {
        return sessionToken;
    }

    private synchronized boolean serverIsFound() {
        return serverIsFound;
    }

    private synchronized void setServerIsFound(boolean found) {
        serverIsFound = found;
    }

    private void startSensorJobExecutor() {
        this.sensorJobExecutor = new SensorJobExecutor();
        this.sensorJobExecutor.start();
    }

    private void registerDigitalSTROMEventListener() {
        if (!serverIsFound()) {
            login();
        }
        this.digitalSTROMEventListener = new DigitalSTROMEventListener();
        this.digitalSTROMEventListener.start();
    }

    /**
     * In order to avoid many sensor readings in a time, this thread starts the
     * jobs, after the old one is finished
     * 
     * @author Alexander Betker
     * @since 1.3.0
     * 
     */
    private class SensorJobExecutor extends Thread {

        private boolean shutdown = false;

        private final int sleepTime = readTimeout;

        @Override
        public void run() {

            while (!this.shutdown) {
                SensorJob job = getHighPriorityJob();

                if (job == null) {
                    job = getMediumPriorityJob();
                    if (job == null)
                        job = getLowPriorityJob();
                }
                if (job != null) {
                    job.execute(digitalSTROM, getSessionToken());
                }

                try {
                    sleep(this.sleepTime);
                } catch (InterruptedException e) {
                    this.shutdown();
                    logger.error("InterruptedException in SensorJobExecutor Thread ... " + e.getStackTrace());
                }
            }
        }

        public synchronized void shutdown() {
            this.shutdown = true;
        }
    }

    /**
     * If someone turns a device or a zone etc. on, we will get a notification
     * to update the state of the item
     * 
     * @author Alexander Betker
     * @since 1.3.0
     * 
     */
    private class DigitalSTROMEventListener extends Thread {

        private boolean shutdown = false;
        private final String EVENT_NAME = "openhabEvent";
        private final int ID = 11;
        private int timeout = 1000;

        private final String INVALID_SESSION = "Invalid session!";// Invalid
        // session!

        private HttpTransport transport = null;
        private JSONResponseHandler handler = null;

        public synchronized void shutdown() {
            this.shutdown = true;
            unsubscribe();
        }

        public DigitalSTROMEventListener() {
            this.handler = new JSONResponseHandler();
            this.transport = new HttpTransport(uri, connectTimeout, readTimeout);
            this.subscribe();
        }

        private void subscribe() {
            if (getSessionToken() != null) {

                boolean transmitted = digitalSTROM.subscribeEvent(getSessionToken(), EVENT_NAME, this.ID,
                        connectTimeout, readTimeout);

                if (!transmitted) {
                    this.shutdown = true;
                    logger.error(
                            "Couldn't subscribe eventListener ... maybe timeout because system is to busy ...");
                }
            } else {
                logger.error("Couldn't subscribe eventListener because there is no token (no connection)");
            }
        }

        @Override
        public void run() {
            while (!this.shutdown) {

                String request = this.getEventAsRequest(this.ID, 500);

                if (request != null) {

                    String response = this.transport.execute(request, 2 * this.timeout, this.timeout);

                    JSONObject responseObj = this.handler.toJSONObject(response);

                    if (this.handler.checkResponse(responseObj)) {
                        JSONObject obj = this.handler.getResultJSONObject(responseObj);

                        if (obj != null
                                && obj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT.getKey()) instanceof JSONArray) {
                            JSONArray array = (JSONArray) obj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT.getKey());
                            try {
                                handleEvent(array);
                            } catch (Exception e) {
                                logger.warn("EXCEPTION in eventListener thread : " + e.getLocalizedMessage());
                            }
                        }
                    } else {
                        String errorStr = null;
                        if (responseObj != null && responseObj
                                .get(JSONApiResponseKeysEnum.EVENT_GET_EVENT_ERROR.getKey()) != null) {
                            errorStr = responseObj.get(JSONApiResponseKeysEnum.EVENT_GET_EVENT_ERROR.getKey())
                                    .toString();
                        }

                        if (errorStr != null && errorStr.equals(this.INVALID_SESSION)) {
                            this.subscribe();
                        } else if (errorStr != null) {
                            logger.error("Unknown error message in event response: " + errorStr);
                        }
                    }
                }
            }
        }

        private String getEventAsRequest(int subscriptionID, int timeout) {
            if (getSessionToken() != null) {
                return JSONRequestConstants.JSON_EVENT_GET + JSONRequestConstants.PARAMETER_TOKEN
                        + getSessionToken() + JSONRequestConstants.INFIX_PARAMETER_SUBSCRIPTION_ID + subscriptionID
                        + JSONRequestConstants.INFIX_PARAMETER_TIMEOUT + timeout;
            }
            return null;
        }

        private boolean unsubscribeEvent(String name, int subscriptionID) {
            if (getSessionToken() != null) {
                return digitalSTROM.unsubscribeEvent(getSessionToken(), EVENT_NAME, this.ID, connectTimeout,
                        readTimeout);
            }
            return false;
        }

        private boolean unsubscribe() {
            return this.unsubscribeEvent(this.EVENT_NAME, this.ID);
        }

        private void handleEvent(JSONArray array) {
            if (array.size() > 0) {
                Event event = new JSONEventImpl(array);

                for (EventItem item : event.getEventItems()) {
                    if (item.getName() != null && item.getName().equals(this.EVENT_NAME)) {
                        handleOpenhabEvent(item);
                    }
                }
            }
        }
    }

    @Override
    public void deviceUpdated(String dsid) {
        List<String> itemNames = getItemNamesForDsid(dsid);
        if (itemNames != null) {
            for (String itemName : itemNames) {
                updateItemState(getConfigForItemName(itemName).item);
            }
        }
    }

    private void updateItemState(Item item) {
        if (item != null) {
            Device device = deviceMap.get(item.getName());

            if (device != null) {

                State state = null;
                if (item instanceof DimmerItem) {
                    state = new PercentType(getPercent(device.getMaxOutPutValue(), device.getOutputValue()));

                } else if (item instanceof SwitchItem && !(item instanceof DimmerItem)) {

                    state = device.getOutputValue() > 0 ? OnOffType.ON : OnOffType.OFF;
                } else if (item instanceof NumberItem) {

                    DigitalSTROMBindingConfig confItem = getConfigForItemName(item.getName());
                    if (confItem != null) {

                        if (confItem.sensor != null) {
                            int value = -1;
                            switch (confItem.sensor) {

                            case TEMPERATURE_INDOORS:
                                value = device.getTemperatureSensorValue();

                                if (value != -1) {
                                    // Celsius 
                                    state = new DecimalType((double) (value) / 40 - 43.15); // TODO use resolution from sensor
                                }
                                break;
                            case RELATIVE_HUMIDITY_INDOORS:
                                value = device.getHumiditySensorValue();

                                if (value != -1) {
                                    // Percent
                                    state = new DecimalType((double) (value) / 40); // TODO use resolution from sensor
                                }
                                break;
                            case TEMPERATURE_OUTDOORS:
                                value = device.getTemperatureSensorValue();

                                if (value != -1) {
                                    // Celsius 
                                    state = new DecimalType((double) (value) / 40 - 43.15); // TODO use resolution from sensor
                                }
                                break;
                            case RELATIVE_HUMIDITY_OUTDOORS:
                                value = device.getHumiditySensorValue();

                                if (value != -1) {
                                    // Percent
                                    state = new DecimalType((double) (value) / 40); // TODO use resolution from sensor
                                }
                                break;
                            default:
                                break;

                            }
                        } else if (confItem.consumption != null) {
                            int value = -1;
                            switch (confItem.consumption) {

                            case ACTIVE_POWER:
                                value = device.getPowerConsumption();
                                break;
                            case OUTPUT_CURRENT:
                                value = device.getEnergyMeterValue();

                                if (value != -1) {
                                    state = new DecimalType(value);
                                }
                                break;
                            default:
                                break;

                            }
                        }
                    }
                } else if (item instanceof RollershutterItem) {

                    DigitalSTROMBindingConfig confItem = getConfigForItemName(item.getName());
                    if (confItem != null) {

                        if (confItem.context != null && confItem.context.equals(ContextConfig.slat)) {

                            int output = getPercent(device.getMaxSlatPosition(), device.getSlatPosition());

                            state = new PercentType(100 - output);
                        } else if (confItem.context != null && confItem.context.equals(ContextConfig.awning)) {

                            int output = getPercent(device.getMaxOutPutValue(), device.getOutputValue());

                            state = new PercentType(output);
                        } else {
                            int output = getPercent(device.getMaxOutPutValue(), device.getOutputValue());

                            state = new PercentType(100 - output);
                        }
                    }
                } else if (item instanceof StringItem) {
                    DigitalSTROMBindingConfig confItem = getConfigForItemName(item.getName());
                    if (confItem != null) {

                        if (confItem.sensor != null) {
                            int value = -1;
                            switch (confItem.sensor) {

                            case TEMPERATURE_INDOORS:
                                value = device.getTemperatureSensorValue();

                                if (value != -1) {
                                    // Celsius 
                                    state = new DecimalType((double) (value) / 40 - 43.15); // TODO use resolution from sensor
                                }
                                break;
                            case RELATIVE_HUMIDITY_INDOORS:
                                value = device.getHumiditySensorValue();

                                if (value != -1) {
                                    // Percent
                                    state = new DecimalType((double) (value) / 40); // TODO use resolution from sensor
                                }
                                break;
                            case TEMPERATURE_OUTDOORS:
                                value = device.getTemperatureSensorValue();

                                if (value != -1) {
                                    // Celsius 
                                    state = new DecimalType((double) (value) / 40 - 43.15); // TODO use resolution from sensor
                                }
                                break;
                            case RELATIVE_HUMIDITY_OUTDOORS:
                                value = device.getHumiditySensorValue();

                                if (value != -1) {
                                    // Percent
                                    state = new DecimalType((double) (value) / 40); // TODO use resolution from sensor
                                }
                                break;
                            default:
                                break;
                            }
                        } else if (confItem.consumption != null) {
                            int value = -1;

                            switch (confItem.consumption) {

                            case ACTIVE_POWER:
                                value = device.getPowerConsumption();

                                if (value != -1) {
                                    state = new DecimalType(value);
                                }
                                break;
                            case OUTPUT_CURRENT:
                                value = device.getEnergyMeterValue();

                                if (value != -1) {
                                    state = new DecimalType(value);
                                }
                                break;
                            default:
                                break;

                            }
                        }
                    }
                }

                eventPublisher.postUpdate(item.getName(), state);
            } else {
                logger.error("couldn't update item state, because device is null: " + item.getName());
            }
        } else {
            logger.error("couldn't update item state, because item is null");
        }

    }

    private int getPercent(int max, int val) {
        if (withParameterMax(max) && withParameterValue(val)) {
            return (100 * val / max);
        }
        return -1;
    }

    private boolean withParameterMax(int max) {
        return (max > 0);

    }

    private boolean withParameterValue(int value) {
        return (value > -1);
    }

    @Override
    public void bindingChanged(BindingProvider provider, String itemName) {
        if (provider instanceof DigitalSTROMBindingProvider) {
            //remove device associated with the item
            Device device = deviceMap.get(itemName);
            if (device != null) {
                List<String> itemNamesForDsid = getItemNamesForDsid(device.getDSID().getValue());
                if (itemNamesForDsid.size() == 1) {
                    device.removeDeviceListener(this);
                    removeSensorJobs(device.getDSID());
                }
            }
            deviceMap.remove(itemName);

            //initialize the device
            DigitalSTROMBindingConfig confItem = getConfigForItemName(itemName);
            if (confItem != null && confItem.dsid != null) {
                if (rawDsidToDeviceMap.size() == 0 && serverIsFound()) {
                    rawDsidToDeviceMap = getAllDigitalSTROMDevicesMap();
                }
                device = rawDsidToDeviceMap.get(confItem.dsid.getValue());
                if (device != null) {
                    addDevice(itemName, device);
                    updateItemState(confItem.item);
                    handleStructure(digitalSTROM.getApartmentStructure(getSessionToken()));
                }
            }
        }
    }

}