Package org.openhab.binding.tinkerforge.internal

Source Code of org.openhab.binding.tinkerforge.internal.TinkerforgeBinding

/**
* 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.tinkerforge.internal;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.openhab.binding.tinkerforge.TinkerforgeBindingProvider;
import org.openhab.binding.tinkerforge.internal.config.ConfigurationHandler;
import org.openhab.binding.tinkerforge.internal.model.ColorActor;
import org.openhab.binding.tinkerforge.internal.model.DigitalActor;
import org.openhab.binding.tinkerforge.internal.model.Ecosystem;
import org.openhab.binding.tinkerforge.internal.model.GenericDevice;
import org.openhab.binding.tinkerforge.internal.model.IODevice;
import org.openhab.binding.tinkerforge.internal.model.MBaseDevice;
import org.openhab.binding.tinkerforge.internal.model.MBrickd;
import org.openhab.binding.tinkerforge.internal.model.MDevice;
import org.openhab.binding.tinkerforge.internal.model.MInSwitchActor;
import org.openhab.binding.tinkerforge.internal.model.MSensor;
import org.openhab.binding.tinkerforge.internal.model.MSubDevice;
import org.openhab.binding.tinkerforge.internal.model.MSubDeviceHolder;
import org.openhab.binding.tinkerforge.internal.model.MSwitchActor;
import org.openhab.binding.tinkerforge.internal.model.MTFConfigConsumer;
import org.openhab.binding.tinkerforge.internal.model.MTextActor;
import org.openhab.binding.tinkerforge.internal.model.ModelFactory;
import org.openhab.binding.tinkerforge.internal.model.ModelPackage;
import org.openhab.binding.tinkerforge.internal.model.NumberActor;
import org.openhab.binding.tinkerforge.internal.model.OHConfig;
import org.openhab.binding.tinkerforge.internal.model.OHTFDevice;
import org.openhab.binding.tinkerforge.internal.model.TFConfig;
import org.openhab.binding.tinkerforge.internal.types.DecimalValue;
import org.openhab.binding.tinkerforge.internal.types.HighLowValue;
import org.openhab.binding.tinkerforge.internal.types.OnOffValue;
import org.openhab.binding.tinkerforge.internal.types.TinkerforgeValue;
import org.openhab.binding.tinkerforge.internal.types.UnDefValue;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.ContactItem;
import org.openhab.core.library.items.NumberItem;
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.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.StringType;
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;

/**
* This binding connects the TinkerForge devices to the openhab eventbus.
*
* This class uses an EMF model with a TinkerforgeEcosystem object as root. The TinkerforgeEcosystem
* has Brickd child objects. The Brickd object has an IpConnection to the TinkerForge brickd daemon,
* identified by an ip address or host name and a port. The Brickd object has child objects for
* every connected TinkerForge device. The TinkerForge device object holds leaf objects for
* subdevices if available. All the device objects are sharing the IpConnection of the Brickd
* object. The EMF device objects can be interpreted as a facade for the TinkerForge api device
* objects. If available, the EMF device objects implement TinkerForge CallbackListeners for sensor
* value updates and are updating the EMF device object sensor properties accordingly.
*
* The binding adds a listener to the TinkerforgeEcosystem. On the one hand this listener handles
* updated sensor values and propagates them to the openhab eventbus. On the other hand the listener
* is informed about new devices in the TinkerforgeEcosystem and thus can pass configuration
* settings from openhab.cfg to the devices. The callback period of the CallbackListener and a
* threshold value are configurable through openhab.cfg.
*
* All device values are additionally polled by the execute method mainly to get values from
* subdevices which don't have TinkerForge CallbackListeners for getting the sensor values.
*
* Tinkerforge devices which work as actors like relays can be controlled with this binding.
*
* For now only a subset of the TinkerForge devices are supported and not all features of the
* devices are implemented. More devices and features will be added soon. The following devices are
* supported for now:
* <ul>
* <li>Servo Brick</li>
* <li>DC Brick</li>
* <li>Dual Relay Bricklet</li>
* <li>Humidity Bricklet</li>
* <li>Distance IR Bricklet</li>
* <li>Temperature Bricklet</li>
* <li>Barometer Bricklet</li>
* <ul>
* <li>Barometer</li>
* <li>Temperature Device</li>
* </ul>
* <li>Ambient Light Bricklet</li> <li>LCD</li>
* <ul>
* <li>LCD 20×4 Bricklet</li>
* <li>4 Buttons</li>
* </ul>
* </ul>
*
* @author Theo Weiss
* @since 1.3.0
*/
public class TinkerforgeBinding extends AbstractActiveBinding<TinkerforgeBindingProvider>
    implements
      ManagedService {

  private static final String CONFIG_KEY_HOSTS = "hosts";

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

  private static final int BRICKD_DEFAULT_PORT = 4223;

  /**
   * the refresh interval which is used to poll values from the Tinkerforge server (optional,
   * defaults to 60000ms)
   */
  private long refreshInterval = 60000;

  private Ecosystem tinkerforgeEcosystem;

  private ModelFactory modelFactory;
  private OHConfig ohConfig;
  private boolean isConnected;

  public TinkerforgeBinding() {
    modelFactory = ModelFactory.eINSTANCE;
  }

  @Override
  public void activate() {}

  @Override
  public void deactivate() {
    disconnectModel();
  }

  /**
   * Disconnects the IpConnections to all TinkerForge brickds and destroys the TinkerforgeEcosystem.
   */
  private void disconnectModel() {
    if (isConnected) {
      logger.debug("disconnect called");
      tinkerforgeEcosystem.disconnect();
      tinkerforgeEcosystem = null;
      isConnected = false;
    }
  }

  /**
   * Creates a Tinkerforge Ecosystem object and adds a listener to it.
   */
  private void connectModel() {
    tinkerforgeEcosystem = modelFactory.createEcosystem();
    listen2Model(tinkerforgeEcosystem);
    logger.debug("{} connectModel called", LoggerConstants.TFINIT);
    isConnected = true;
  }

  /**
   * Searches for a brickd with the given {@code host} and {@code port} in the Ecosystem. If there
   * is no brickd found a new Brickd object is created, added to the Ecosystem an the IpConnection
   * to the Tinkerforge brickd is established and a device enumeration is triggert.
   *
   * @param host The host name or ip address of the TinkerForge brickd as String.
   * @param port The port of the TinkerForge brickd as int.
   */
  private void connectBrickd(String host, int port) {
    MBrickd brickd = tinkerforgeEcosystem.getBrickd(host, port);
    if (brickd == null) {
      brickd = modelFactory.createMBrickd();
      brickd.setHost(host);
      brickd.setPort(port);
      brickd.setEcosystem(tinkerforgeEcosystem);
      tinkerforgeEcosystem.getMbrickds().add(brickd);
      brickd.init();
      brickd.connect();
      logger.debug("{} Tinkerforge new brickd for host: {}", LoggerConstants.TFINIT, host);
    } else {
      logger.debug("{} Tinkerforge found existing brickd for host: {}", LoggerConstants.TFINIT,
          host);
    }
  }

  /**
   * Adds a listener {@link EContentAdapter} to the {@link Ecosystem}. The listener handles updated
   * sensor values and posts them to the openhab eventbus by
   * {@link #processTFDeviceValues(Notification) processTFDeviceValues}. Furthermore the addition
   * and removal of devices is handled by {@link #initializeTFDevices(Notification)
   * initializeTFDevices}.
   *
   * @param tinkerforgeEcosystem The EMF Ecosystem object.
   */
  private void listen2Model(Ecosystem tinkerforgeEcosystem) {
    EContentAdapter modelAdapter = new EContentAdapter() {
      @Override
      public void notifyChanged(Notification notification) {
        super.notifyChanged(notification);
        logger.debug("TinkerforgeNotifier was notified");
        if (notification.getEventType() == Notification.ADD
            || notification.getEventType() == Notification.ADD_MANY
            || notification.getEventType() == Notification.REMOVE
            || notification.getEventType() == Notification.REMOVE_MANY) {
          initializeTFDevices(notification);
        } else {
          processTFDeviceValues(notification);
        }
      }

    };
    tinkerforgeEcosystem.eAdapters().add(modelAdapter);
  }

  private boolean checkDuplicateGenericDevice(GenericDevice device, String uid, String subId) {
    boolean isDuplicate = false;
    final String genericDeviceId = device.getGenericDeviceId();
    final EList<MSubDevice<?>> genericDevicesList =
        tinkerforgeEcosystem.getDevices4GenericId(uid, genericDeviceId);
    if (genericDevicesList.size() != 0) {
      for (MSubDevice<?> gd : genericDevicesList) {
        if (!gd.getSubId().equals(subId) && gd.getEnabledA().get()) {
          isDuplicate = true;
          logger.error("{} existing device is uid {} subId {}", LoggerConstants.CONFIG,
              gd.getUid(), gd.getSubId());
        }
      }
    }

    return isDuplicate;
  }

  /**
   * Configures and enables newly found devices. For sub devices the master device is also enabled.
   * Configuration is only added if there is a configuration from openhab.cfg available and the
   * device is configurable which is the case for {@link MTFConfigConsumer}. Devices of type
   * {@link IODevice} are only enabled if they are configured in openhab.cfg, all other devices are
   * always enabled.
   *
   * @param device A device object as {@link MBaseDevice}.
   * @param uid The device uid as String.
   * @param subId The device subid as String or <code>null</code> if the device is not a sub device.
   */
  @SuppressWarnings("unchecked")
  private synchronized void addMDevice(MBaseDevice device, String uid, String subId) {
    String logId = subId == null ? uid : uid + " " + subId;
    OHTFDevice<?, ?> deviceConfig = ohConfig.getConfigByTFId(uid, subId);
    if (device.getEnabledA().compareAndSet(false, true)) {
      if (subId != null) {
        MDevice<?> masterDevice = (MDevice<?>) device.eContainer();
        // recursion for adding the master device
        if (!masterDevice.getEnabledA().get()) {
          logger.debug("{} enabling masterDevice {}", LoggerConstants.TFINITSUB,
              masterDevice.getUid());
          addMDevice(masterDevice, uid, null);
        }
      }
      if (device instanceof MTFConfigConsumer<?> && deviceConfig != null) {
        logger.debug("{} found MTFConfigConsumer id {}", LoggerConstants.TFINIT, logId);
        if (device instanceof GenericDevice
            && checkDuplicateGenericDevice((GenericDevice) device, uid, subId)) {
          logger
              .error(
                  "{} ignoring duplicate device uid: {}, subId {}, genericId {}. Fix your openhab.cfg!",
                  LoggerConstants.CONFIG, uid, subId);
          device.getEnabledA().compareAndSet(true, false);
        } else {
          TFConfig deviceTfConfig = EcoreUtil.copy(deviceConfig.getTfConfig());
          logger.debug("{} setting tfConfig for {}", LoggerConstants.TFINIT, logId);
          ((MTFConfigConsumer<EObject>) device).setTfConfig(deviceTfConfig);
          device.enable();
          logger.debug("{} adding/enabling device {} with config: {}", LoggerConstants.TFINIT,
              logId, deviceTfConfig);
        }
      } else if (device instanceof IODevice) {
        logger.debug("{} ignoring unconfigured  IODevice: {}", LoggerConstants.TFINIT, logId);
        // set the device disabled, this is needed for not getting
        // states
        // through execute method
        device.getEnabledA().compareAndSet(true, false);
      } else {
        device.enable();
        logger.debug("{} adding/enabling device: {}", LoggerConstants.TFINIT, logId);
      }
    }
  }

  /**
   * Adds or removes a device to / from the Ecosystem. Notifications from {@link MBrickd} are used
   * for adding devices (not sub devices) and removing of devices and their corresponding sub
   * devices.
   *
   * Notifications from {@link MSubDeviceHolder} for adding sub devices.
   *
   * @param notification The {@link Notification} for add and remove events to the {@link Ecosystem}
   *        .
   */
  private void initializeTFDevices(Notification notification) {
    logger.debug("{} notifier {}", LoggerConstants.TFINIT, notification.getNotifier());
    if (notification.getNotifier() instanceof MBrickd) {
      logger.debug("{} notifier is Brickd", LoggerConstants.TFINIT);
      int featureID = notification.getFeatureID(MBrickd.class);
      if (featureID == ModelPackage.MBRICKD__MDEVICES) {
        if (notification.getEventType() == Notification.ADD) {
          MDevice<?> mDevice = (MDevice<?>) notification.getNewValue();
          addMDevice(mDevice, mDevice.getUid(), null);
        } else if (notification.getEventType() == Notification.ADD_MANY) {
          logger.debug("{} Notifier: add many called: ", LoggerConstants.TFINIT);
        } else if (notification.getEventType() == Notification.REMOVE) {
          logger.debug("{} Notifier: remove called: ", LoggerConstants.TFINIT);
          if (notification.getOldValue() instanceof MBaseDevice) {
            logger.debug("{} Notifier: remove called for MBaseDevice", LoggerConstants.TFINIT);
            MBaseDevice mDevice = (MBaseDevice) notification.getOldValue();
            String uid = mDevice.getUid();
            String subId = null;
            if (searchConfiguredItemName(uid, subId) != null) {
              logger.debug("{} Notifier: removing device: uid {} subid {}", LoggerConstants.TFINIT,
                  uid, subId);
              postUpdate(uid, subId, UnDefValue.UNDEF);
            }
          } else {
            logger.debug("{} unknown notification from mdevices {}", LoggerConstants.TFINIT,
                notification);
          }
        }
      } else {
        logger.debug("{} Notifier: unknown feature {}", LoggerConstants.TFINIT,
            notification.getFeature());
      }
    } else if (notification.getNotifier() instanceof MSubDeviceHolder<?>) {
      int featureID = notification.getFeatureID(MSubDeviceHolder.class);
      if (featureID == ModelPackage.MSUB_DEVICE_HOLDER__MSUBDEVICES) {
        logger.debug("{} MSubdevices Notifier called", LoggerConstants.TFINITSUB);
        if (notification.getEventType() == Notification.ADD) {
          MSubDevice<?> mSubDevice = (MSubDevice<?>) notification.getNewValue();
          addMDevice(mSubDevice, mSubDevice.getUid(), mSubDevice.getSubId());

        }
        if (notification.getEventType() == Notification.REMOVE) {
          logger.debug("{} remove notification from subdeviceholder", LoggerConstants.TFINIT);
          logger.debug("{} Notifier: remove called for MSubDevice", LoggerConstants.TFINIT);
          MSubDevice<?> mDevice = (MSubDevice<?>) notification.getOldValue();
          String uid = mDevice.getUid();
          String subId = mDevice.getSubId();
          if (searchConfiguredItemName(uid, subId) != null) {
            logger.debug("{} Notifier: removing device: uid {} subid {}", LoggerConstants.TFINIT,
                uid, subId);
            postUpdate(uid, subId, UnDefValue.UNDEF);
          }
        }
      }
    } else {
      logger.debug("{} unhandled notifier {}", LoggerConstants.TFINIT, notification.getNotifier());
    }
  }

  /**
   * Processes change events from the {@link Ecosystem}. Sensor values from {@link MSensor} are
   * handled by {@link #processSensorValue(MSensor, Notification) processSensorValue}, actor values
   * from {@link MSwitchActore} are handled by
   * {@link #processSwitchActorValue(MSwitchActor, Notification) processSwitchActorValue}. (no add
   * or remove events, these are handled in {@link #initializeTFDevices(Notification)
   * initializeTFDevices}).
   *
   *
   * @param notification The {@link Notification} about changes to the {@link Ecosystem}.
   */
  private void processTFDeviceValues(Notification notification) {
    if (notification.getNotifier() instanceof MSensor) {
      MSensor<?> sensor = (MSensor<?>) notification.getNotifier();
      int featureID = notification.getFeatureID(MSensor.class);
      if (featureID == ModelPackage.MSENSOR__SENSOR_VALUE) {
        processValue((MBaseDevice) sensor, notification);
      }
    } else if (notification.getNotifier() instanceof MSwitchActor) {
      MSwitchActor switchActor = (MSwitchActor) notification.getNotifier();
      int featureID = notification.getFeatureID(MSwitchActor.class);
      if (featureID == ModelPackage.MSWITCH_ACTOR__SWITCH_STATE) {
        processValue((MBaseDevice) switchActor, notification);
      }
    } else if (notification.getNotifier() instanceof DigitalActor) {
      DigitalActor actor = (DigitalActor) notification.getNotifier();
      int featureID = notification.getFeatureID(DigitalActor.class);
      if (featureID == ModelPackage.DIGITAL_ACTOR__DIGITAL_STATE) {
        processValue((MBaseDevice) actor, notification);
      }
    } else {
      logger.trace("{} ignored notifier {}", LoggerConstants.TFMODELUPDATE,
          notification.getNotifier());
    }
  }

  /**
   * Processes changed device values to post them to the openHAB event bus.
   *
   * @param device The {@link MBaseDevice} device, which has a changed value.
   * @param notification The {@link Notification} about changes to the {@link Ecosystem}.
   */
  private void processValue(MBaseDevice device, Notification notification) {
    TinkerforgeValue newValue = (TinkerforgeValue) notification.getNewValue();
    String uid = device.getUid();
    String subId = null;
    if (device instanceof MSubDevice<?>) {
      subId = ((MSubDevice<?>) device).getSubId();
      logger.trace("{} Notifier found MSubDevice sensor value for: {}",
          LoggerConstants.TFMODELUPDATE, subId);
    } else {
      logger.trace("{} Notifier found mDevice sensor value for: {}", LoggerConstants.TFMODELUPDATE,
          uid);
    }
    postUpdate(uid, subId, newValue);
  }


  /**
   * Searches the name of an item which is bound to the device with the given uid and subid.
   *
   * @param uid The device uid as {@code String}.
   * @param subId The device subid as {@code String} or {@code null} if it is not a sub device.
   * @return The name of the item which is bound to the device as {@code String} or {@code null} if
   *         no item was found.
   */
  private String searchConfiguredItemName(String uid, String subId) {
    for (TinkerforgeBindingProvider provider : providers) {
      for (String itemName : provider.getItemNames()) {
        String deviceUid = provider.getUid(itemName);
        String subDeviceId = provider.getSubId(itemName);
        String deviceName = provider.getName(itemName);
        if (deviceName != null) {
          logger.trace("found item for command: name {}", deviceName);
          OHTFDevice<?, ?> ohtfDevice = ohConfig.getConfigByOHId(deviceName);
          deviceUid = ohtfDevice.getUid();
          deviceName = ohtfDevice.getSubid();
        }
        if (uid.equals(deviceUid)) {
          if (subId == null && subDeviceId == null) {
            return itemName;
          } else if (subId != null && subId.equals(subDeviceId)) {
            return itemName;
          }
        }
      }
    }
    return null;
  }

  /**
   * Searches the provider which is bound to the device with the given uid and subid.
   *
   * @param uid The device uid as {@code String}.
   * @param subId The device subid as {@code String} or {@code null} if it is not a sub device.
   * @return The {@code TinkerforgeBindingProvider} which is bound to the device as {@code Item} or
   *         {@code null} if no item was found.
   */
  private Map<String, TinkerforgeBindingProvider> getBindingProviders(String uid, String subId) {
    Map<String, TinkerforgeBindingProvider> providerMap =
        new HashMap<String, TinkerforgeBindingProvider>();
    for (TinkerforgeBindingProvider provider : providers) {
      for (String itemName : provider.getItemNames()) {
        String deviceUid = provider.getUid(itemName);
        String subDeviceId = provider.getSubId(itemName);
        String deviceName = provider.getName(itemName);
        if (deviceName != null) {
          OHTFDevice<?, ?> ohtfDevice = ohConfig.getConfigByOHId(deviceName);
          deviceUid = ohtfDevice.getUid();
          subDeviceId = ohtfDevice.getSubid();
          logger.trace("found deviceName {}, uid={}, subId {}", deviceName, deviceUid, subDeviceId);
        }
        if (uid.equals(deviceUid)) {
          if (subId == null && subDeviceId == null) {
            providerMap.put(itemName, provider);
          } else if (subId != null && subId.equals(subDeviceId)) {
            providerMap.put(itemName, provider);
          }
        }
      }
    }
    return providerMap;
  }

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

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

  /**
   * The working method which is called by the refresh thread.
   *
   * Triggers an update of state values for all devices. The update is propagated through the
   * {@link Ecosystem} listeners. All OutActors are ignored, they may only send updates if the
   * hardware device has updates (think of a pressed switch).
   *
   */
  @Override
  protected void execute() {
    for (TinkerforgeBindingProvider provider : providers) {
      for (String itemName : provider.getItemNames()) {
        updateItemValues(provider, itemName, true);
      }
    }
  }

  /**
   * Triggers an update of state values for all devices.
   *
   * @param provider The {@code TinkerforgeBindingProvider} which is bound to the device as
   *        {@code Item}
   * @param itemName The name of the {@code Item} as String
   * @param only_poll_enabled Fetch only the values of devices which do not support callback
   *        listeners. These devices are marked with poll "true" flag.
   */
  protected void updateItemValues(TinkerforgeBindingProvider provider, String itemName,
      boolean only_poll_enabled) {
    String deviceUid = provider.getUid(itemName);
    Item item = provider.getItem(itemName);
    String deviceSubId = provider.getSubId(itemName);
    String deviceName = provider.getName(itemName);
    if (deviceName != null) {
      String[] ids = getDeviceIdsForDeviceName(deviceName);
      deviceUid = ids[0];
      deviceSubId = ids[1];
    }
    MBaseDevice mDevice = tinkerforgeEcosystem.getDevice(deviceUid, deviceSubId);
    if (mDevice != null && mDevice.getEnabledA().get()) {
      if (only_poll_enabled && !mDevice.isPoll()) {
        // do nothing
        logger.debug("{} omitting fetch value for no poll{}:{}", LoggerConstants.ITEMUPDATE,
            deviceUid, deviceSubId);
      } else {
        if (mDevice instanceof MSensor) {
          ((MSensor<?>) mDevice).fetchSensorValue();
        } else if (mDevice instanceof MInSwitchActor && item instanceof SwitchItem) {
          ((MInSwitchActor) mDevice).fetchSwitchState();
        } else if (mDevice instanceof DigitalActor) {
          ((DigitalActor) mDevice).fetchDigitalValue();
        }
      }
    }
  }

  @Override
  public void bindingChanged(BindingProvider provider, String itemName) {
    logger.debug("{} bindingChanged item {}", LoggerConstants.ITEMUPDATE, itemName);
    updateItemValues((TinkerforgeBindingProvider) provider, itemName, false);
  }

  private void postUpdate(String uid, String subId, TinkerforgeValue sensorValue) {
    // TODO undef handling
    Map<String, TinkerforgeBindingProvider> providerMap = getBindingProviders(uid, subId);
    if (providerMap.size() == 0) {
      logger.debug("{} found no item for uid {}, subid {}", LoggerConstants.TFMODELUPDATE, uid,
          subId);
    }
    for (Entry<String, TinkerforgeBindingProvider> entry : providerMap.entrySet()) {
      String itemName = entry.getKey();
      TinkerforgeBindingProvider provider = entry.getValue();
      Class<? extends Item> itemType = provider.getItemType(itemName);
      State value = UnDefType.UNDEF;
      if (sensorValue instanceof DecimalValue) {
        if (itemType.isAssignableFrom(NumberItem.class)
            || itemType.isAssignableFrom(StringItem.class)) {
          value = DecimalType.valueOf(String.valueOf(sensorValue));
        } else if (itemType.isAssignableFrom(ContactItem.class)) {
          value =
              sensorValue.equals(DecimalValue.ZERO) ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
        } else if (itemType.isAssignableFrom(SwitchItem.class)) {
          value = sensorValue.equals(DecimalValue.ZERO) ? OnOffType.OFF : OnOffType.ON;
        }
      } else if (sensorValue instanceof HighLowValue) {
        if (itemType.isAssignableFrom(NumberItem.class)
            || itemType.isAssignableFrom(StringItem.class)) {
          value =
              sensorValue == HighLowValue.HIGH ? DecimalType.valueOf("1") : DecimalType
                  .valueOf("0");
        } else if (itemType.isAssignableFrom(ContactItem.class)) {
          value = sensorValue == HighLowValue.HIGH ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
        } else if (itemType.isAssignableFrom(SwitchItem.class)) {
          value = sensorValue == HighLowValue.HIGH ? OnOffType.ON : OnOffType.OFF;
        } else {
          logger.error("{} unsupported item type {} for item {}", LoggerConstants.TFMODELUPDATE,
              provider.getItem(itemName), itemName);
        }
      } else if (sensorValue instanceof OnOffValue) {
        if (itemType.isAssignableFrom(NumberItem.class)
            || itemType.isAssignableFrom(StringItem.class)) {
          value =
              sensorValue == OnOffValue.ON ? DecimalType.valueOf("1") : DecimalType.valueOf("0");
        } else if (itemType.isAssignableFrom(ContactItem.class)) {
          value = sensorValue == OnOffValue.ON ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
        } else if (itemType.isAssignableFrom(SwitchItem.class)) {
          value = sensorValue == OnOffValue.ON ? OnOffType.ON : OnOffType.OFF;
        } else {
          logger.error("{} unsupported item type {} for item {}", LoggerConstants.TFMODELUPDATE,
              provider.getItem(itemName), itemName);
        }
      } else if (sensorValue == UnDefValue.UNDEF || sensorValue == null) {
        value = UnDefType.UNDEF;
      }
      eventPublisher.postUpdate(itemName, value);
      logger.debug("{} postupdate: found sensorValue: {} for item {}",
          LoggerConstants.TFMODELUPDATE, sensorValue, itemName);
    }
  }

  /**
   * Gets the uid and the subid of a device from the openhab.cfg, using the device name as input.
   *
   * @param deviceName The symbolic device name as {@code String}.
   * @return A String array with the device uid as first element as {@code String} and the device
   *         subid as second element as {@code String} or {@code null}.
   */
  private String[] getDeviceIdsForDeviceName(String deviceName) {
    logger.trace("searching ids for name {}", deviceName);
    OHTFDevice<?, ?> ohtfDevice = ohConfig.getConfigByOHId(deviceName);
    String[] ids = {ohtfDevice.getUid(), ohtfDevice.getSubid()};
    return ids;
  }

  /**
   * {@inheritDoc}
   *
   * Searches the item with the given {@code itemName} in the {@link TinkerforgeBindingProvider}
   * collection and gets the uid and subid of the device. The appropriate device is searched in the
   * ecosystem and the command is executed on the device.
   *
   * {@code OnOffType} commands are executed on {@link MInSwitchActor} objects. {@code StringType}
   * commands are executed on {@link MTextActor} objects.
   *
   */
  @Override
  protected void internalReceiveCommand(String itemName, Command command) {
    logger.debug("received command {} for item {}", command, itemName);
    for (TinkerforgeBindingProvider provider : providers) {
      for (String itemNameP : provider.getItemNames()) {
        if (itemNameP.equals(itemName)) {
          String deviceUid = provider.getUid(itemName);
          String deviceSubId = provider.getSubId(itemName);
          String deviceName = provider.getName(itemName);
          if (deviceName != null) {
            String[] ids = getDeviceIdsForDeviceName(deviceName);
            deviceUid = ids[0];
            deviceSubId = ids[1];
          }
          logger.trace("{} found item for command: uid: {}, subid: {}", LoggerConstants.COMMAND,
              deviceUid, deviceSubId);
          MBaseDevice mDevice = tinkerforgeEcosystem.getDevice(deviceUid, deviceSubId);
          if (mDevice != null && mDevice.getEnabledA().get()) {
            if (command instanceof OnOffType) {
              logger.trace("{} found onoff command", LoggerConstants.COMMAND);
              if (mDevice instanceof MInSwitchActor) {
                OnOffType cmd = (OnOffType) command;
                OnOffValue state = cmd == OnOffType.OFF ? OnOffValue.OFF : OnOffValue.ON;
                ((MSwitchActor) mDevice).turnSwitch(state);
              } else if (mDevice instanceof DigitalActor) {
                OnOffType cmd = (OnOffType) command;
                HighLowValue state = cmd == OnOffType.OFF ? HighLowValue.LOW : HighLowValue.HIGH;
                ((DigitalActor) mDevice).turnDigital(state);
              } else {
                logger.error("{} received OnOff command for non-SwitchActor",
                    LoggerConstants.COMMAND);
              }
            } else if (command instanceof StringType) {
              logger.trace("{} found string command", LoggerConstants.COMMAND);
              if (mDevice instanceof MTextActor) {
                ((MTextActor) mDevice).setText(command.toString());
              }
            } else if (command instanceof DecimalType) {
              logger.debug("{} found number command", LoggerConstants.COMMAND);
              if (command instanceof HSBType) {
                logger.debug("{} found HSBType command", LoggerConstants.COMMAND);
                if (mDevice instanceof ColorActor) {
                  ((ColorActor) mDevice).setColor((HSBType) command,
                      provider.getDeviceOptions(itemName));
                }
              } else {
                if (mDevice instanceof NumberActor) {
                  ((NumberActor) mDevice).setNumber(((DecimalType) command).toBigDecimal());
                }
              }
            } else {
              logger.error("{} got unknown command type: {}", LoggerConstants.COMMAND,
                  command.toString());
            }
          } else
            logger.error("{} no tinkerforge device found for command for item uid: {} subId: {}",
                LoggerConstants.COMMAND, deviceUid, deviceSubId);
        }
      }
    }
  }

  /**
   * Updates the configuration of the managed service.
   *
   * Extracts the host and port configuration and connects the appropriate brickds.
   *
   * The device configuration from openhab.cfg is parsed into a {@code Map} based (temporary)
   * structure. This structure is used to generate the {@link OHConfig} EMF model configuration
   * store.
   */
  @Override
  public void updated(Dictionary<String, ?> config) throws ConfigurationException {
    if (config != null) {
      if (isConnected) {
        disconnectModel();
      }
      connectModel();
      String refreshIntervalString = (String) config.get("refresh");
      if (StringUtils.isNotBlank(refreshIntervalString)) {
        refreshInterval = Long.parseLong(refreshIntervalString);
      }

      ConfigurationHandler configurationHandler = new ConfigurationHandler();
      ohConfig = configurationHandler.createConfig(config);

      // read further config parameters here ...
      logger.debug("{} updated called", LoggerConstants.CONFIG);
      // must be done after all other config has been processed
      String cfgHostsLine = (String) config.get(CONFIG_KEY_HOSTS);
      parseCfgHostsAndConnect(cfgHostsLine);
      setProperlyConfigured(true);
    }
  }

  /**
   * Parses the the hosts line from openhab.cfg into hosts and port parts and connects the
   * appropriate brickds by calling {@link #connectBrickd(String, int) connectBrickd}.
   *
   * @param cfgHostsLine The hosts line found in the openhab.cfg as {@code String}.
   */
  private void parseCfgHostsAndConnect(String cfgHostsLine) {
    String[] cfgHostsEntries = cfgHostsLine.split("\\s");
    for (int i = 0; i < cfgHostsEntries.length; i++) {
      String cfgHostEntry = cfgHostsEntries[i];
      String[] cfgHostAndPort = cfgHostEntry.split(":", 2);
      String host = cfgHostAndPort[0];
      int port;
      if (cfgHostAndPort.length == 2) {
        port = Integer.parseInt(cfgHostAndPort[1]);
      } else {
        port = BRICKD_DEFAULT_PORT;
      }
      connectBrickd(host, port);
    }
  }

}
TOP

Related Classes of org.openhab.binding.tinkerforge.internal.TinkerforgeBinding

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.