Package org.openhab.binding.digitalstrom.internal

Source Code of org.openhab.binding.digitalstrom.internal.DigitalSTROMBinding$DigitalSTROMEventListener

/**
* Copyright (c) 2010-2014, 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.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) {
            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());
          }
        }
      }

    }
  }

  /**
   * @{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 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.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;

              }
            }
          }
        } 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.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()));
        }
      }
    }
  }

}
TOP

Related Classes of org.openhab.binding.digitalstrom.internal.DigitalSTROMBinding$DigitalSTROMEventListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.