/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.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); } protected void addBindingProvider(DigitalSTROMBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(DigitalSTROMBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } /** * {@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) 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())); } } } } }