Package org.openhab.binding.insteonhub.internal

Source Code of org.openhab.binding.insteonhub.internal.InsteonHubBinding$AsyncEventPublisher

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

import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.insteonhub.InsteonHubBindingProvider;
import org.openhab.binding.insteonhub.internal.InsteonHubBindingConfig.BindingType;
import org.openhab.binding.insteonhub.internal.hardware.InsteonHubAdjustmentType;
import org.openhab.binding.insteonhub.internal.hardware.InsteonHubLevelUpdateType;
import org.openhab.binding.insteonhub.internal.hardware.InsteonHubProxy;
import org.openhab.binding.insteonhub.internal.hardware.InsteonHubProxyListener;
import org.openhab.binding.insteonhub.internal.util.InsteonHubBindingConfigUtil;
import org.openhab.core.binding.AbstractActiveBinding;
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.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType;
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;

/**
* Insteon Hub binding. Handles all commands and polls configured devices to
* process updates.
*
* @author Eric Thill
* @since 1.4.0
*/
public class InsteonHubBinding extends
    AbstractActiveBinding<InsteonHubBindingProvider> implements
    ManagedService {

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

  public static final String DEFAULT_HUB_ID = "_default";
  private static final long DEFAULT_REFRESH_INTERVAL = 60000;
  private static final String BINDING_NAME = "InsteonHubBinding";

  private final Map<String, AtomicLong> itemDimTimeouts = Collections
      .synchronizedMap(new HashMap<String, AtomicLong>());
  private long refreshInterval = DEFAULT_REFRESH_INTERVAL;
  private volatile boolean activated;

  // Map of proxies. key=hubId, value=proxy
  // Used to keep track of proxies
  private final Map<String, InsteonHubProxy> proxies = new HashMap<String, InsteonHubProxy>();

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

  @Override
  protected String getName() {
    return "InsteonHub Refresh Service";
  }

  @Override
  protected void execute() {
    logger.debug(BINDING_NAME + " execute");

    Set<InsteonHubBindingDeviceInfo> deviceInfos = InsteonHubBindingConfigUtil
        .getConfiguredDevices(providers);
    // loop through all configured devices
    for (InsteonHubBindingDeviceInfo deviceInfo : deviceInfos) {
      // lookup proxy for device
      InsteonHubProxy proxy = proxies.get(deviceInfo.getHubId());
      if (proxy != null) {
        // request device level from proxy
        // this will callback in AsyncEventPublisher if device exists
        proxy.requestDeviceLevel(deviceInfo.getDeviceId());
      }
    }

    logger.debug(BINDING_NAME + " execute complete");
  }

  private static int levelToPercent(int level) {
    return (int) (100 * level / 255.0);
  }

  @Override
  protected void internalReceiveCommand(String itemName, Command command) {
    // get configuration for this item
    InsteonHubBindingConfig config = InsteonHubBindingConfigUtil
        .getConfigForItem(providers, itemName);
    if (config == null) {
      logger.error(BINDING_NAME + " received command for unknown item '"
          + itemName + "'");
      return;
    }

    // parse info from config
    BindingType type = config.getBindingType();
    String hubId = config.getDeviceInfo().getHubId();
    String deviceId = config.getDeviceInfo().getDeviceId();

    // lookup proxy from this configuration
    InsteonHubProxy proxy = proxies.get(hubId);
    if (proxy == null) {
      logger.error(BINDING_NAME
          + " received command for unknown hub id '" + hubId + "'");
      return;
    }

    if (logger.isDebugEnabled()) {
      logger.debug(BINDING_NAME + " processing command '" + command
          + "' of type '" + command.getClass().getSimpleName()
          + "' for item '" + itemName + "'");
    }

    try {
      // process according to type
      if (type == BindingType.SWITCH) {
        // set value on or off
        if (command instanceof OnOffType) {
          proxy.setDevicePower(deviceId, command == OnOffType.ON);
        }
      } else if (type == BindingType.DIMMER) {
        // INSTEON Dimmer supports Dimmer and RollerShutter types
        if (command instanceof OnOffType) {
          // ON or OFF => Set level to 255 or 0
          int level = command == OnOffType.ON ? 255 : 0;
          proxy.setDeviceLevel(deviceId, level);
        } else if (command instanceof IncreaseDecreaseType) {
          // Increase/Decrease => Incremental Brighten/Dim
          InsteonHubAdjustmentType adjustmentType;
          if (command == IncreaseDecreaseType.INCREASE)
            adjustmentType = InsteonHubAdjustmentType.BRIGHTEN;
          else
            adjustmentType = InsteonHubAdjustmentType.DIM;
          if (setDimTimeout(itemName)) {
            proxy.startDeviceAdjustment(deviceId, adjustmentType);
          }
        } else if (command instanceof UpDownType) {
          // Up/Down => Start Brighten/Dim
          InsteonHubAdjustmentType adjustmentType;
          if (command == UpDownType.UP)
            adjustmentType = InsteonHubAdjustmentType.BRIGHTEN;
          else
            adjustmentType = InsteonHubAdjustmentType.DIM;
          proxy.startDeviceAdjustment(deviceId, adjustmentType);
        } else if (command instanceof StopMoveType) {
          // Stop => Stop Brighten/Dim
          if (command == StopMoveType.STOP) {
            proxy.stopDeviceAdjustment(deviceId);
          }
        } else {
          // set level from 0 to 100 percent value
          byte percentByte = Byte.parseByte(command.toString());
          float percent = percentByte * .01f;
          int level = (int) (255 * percent);
          proxy.setDeviceLevel(deviceId, level);
        }

      }
    } catch (Throwable t) {
      logger.error("Error processing command '" + command
          + "' for item '" + itemName + "'", t);
    }
  }

  // returns true if the timeout was not already set
  private boolean setDimTimeout(String itemName) {
    AtomicLong timeout = itemDimTimeouts.get(itemName);
    if (timeout == null) {
      timeout = new AtomicLong(System.currentTimeMillis() + 400);
      itemDimTimeouts.put(itemName, timeout);
      return true;
    } else {
      long existing = timeout.getAndSet(System.currentTimeMillis() + 400);
      return existing == 0;
    }
  }

  @Override
  public synchronized void updated(Dictionary<String, ?> config)
      throws ConfigurationException {
    logger.debug(BINDING_NAME + " updated");
    try {
      // Process device configuration
      if (config != null) {
        String refreshIntervalString = (String) config.get("refresh");
        if (StringUtils.isNotBlank(refreshIntervalString)) {
          refreshInterval = Long.parseLong(refreshIntervalString);
        }

        // Stop all existing proxy async threads
        for (InsteonHubProxy proxy : proxies.values()) {
          proxy.stop();
        }
        // Clear proxy map. It will be rebuilt.
        proxies.clear();

        // Load new proxies
        Map<String, InsteonHubProxy> newProxies = InsteonHubProxyFactory
            .createInstances(config);
        proxies.putAll(newProxies);
        for (Map.Entry<String, InsteonHubProxy> entry : proxies
            .entrySet()) {
          String hubId = entry.getKey();
          InsteonHubProxy proxy = entry.getValue();
          proxy.addListener(new AsyncEventPublisher(hubId));
          // If activated, start proxy now
          if (activated) {
            proxy.start();
          }
        }

        // Set properly configured
        setProperlyConfigured(true);
      }
    } catch (Throwable t) {
      logger.error("Error configuring " + getName(), t);
      setProperlyConfigured(false);
    }
  }

  @Override
  public synchronized void activate() {
    logger.debug(BINDING_NAME + " activated");
    activated = true;
    dimStopThread.start();
    // start all proxy async threads
    for (InsteonHubProxy proxy : proxies.values()) {
      proxy.start();
    }
  }

  @Override
  public synchronized void deactivate() {
    logger.debug(BINDING_NAME + " deactivated");
    activated = false;
    // stop all proxy async threads
    for (InsteonHubProxy proxy : proxies.values()) {
      proxy.stop();
    }
  }

  @Override
  protected void internalReceiveUpdate(String itemName, State newState) {
    if (logger.isTraceEnabled()) {
      logger.trace(BINDING_NAME + " received update for '" + itemName
          + "' of type '" + newState.getClass().getSimpleName()
          + "' with value '" + newState + "'");
    }
    // ignore
  }

  /**
   * This class listens for updates from the InsteonHubProxy.
   */
  private class AsyncEventPublisher implements InsteonHubProxyListener {

    private final String hubId;

    public AsyncEventPublisher(String hubId) {
      this.hubId = hubId;
    }

    @Override
    public void onLevelUpdate(String device, int level,
        InsteonHubLevelUpdateType updateType) {
      Collection<InsteonHubBindingConfig> configs = InsteonHubBindingConfigUtil
          .getConfigsForDevice(providers, hubId, device);
      for (InsteonHubBindingConfig config : configs) {
        BindingType type = config.getBindingType();
        // FIXME Currently filtering STATUS_CHANGE out for non-dimmer types b/c it's not working properly. Need to learn more.
        if (type == BindingType.SWITCH
            && updateType == InsteonHubLevelUpdateType.STATUS_CHANGE) {
          // switch => 0=OFF, else=ON
          State update = level == 0 ? OnOffType.OFF : OnOffType.ON;
          sendUpdate(config, update);
        } else if (type == BindingType.DIMMER
            && updateType == InsteonHubLevelUpdateType.STATUS_CHANGE) {
          // dimmer => 0-255 to percent
          State update = new PercentType(levelToPercent(level));
          sendUpdate(config, update);
        } else if (type == BindingType.INPUT_ON_OFF
            && updateType == InsteonHubLevelUpdateType.STATUS_CHANGE) {
          // on/off input => translate
          Integer onValue = config.getOnValue();
          Integer offValue = config.getOffValue();
          State update = parseDigitalUpdate(level, onValue, offValue,
              OnOffType.ON, OnOffType.OFF);
          sendUpdate(config, update);
        } else if (type == BindingType.INPUT_OPEN_CLOSED
            && updateType == InsteonHubLevelUpdateType.STATUS_CHANGE) {
          // open/closed input => translate
          Integer openValue = config.getOpenValue();
          Integer closedValue = config.getClosedValue();
          State update = parseDigitalUpdate(level, openValue,
              closedValue, OpenClosedType.OPEN,
              OpenClosedType.CLOSED);
          sendUpdate(config, update);
        } else if (type == BindingType.INPUT_UBYTE) {
          // analog byte value => 0-255
          sendUpdate(config, new DecimalType(level));
        } else if (type == BindingType.INPUT_PERCENT) {
          // analog percentage => 0-255 to percent
          sendUpdate(config, new PercentType(levelToPercent(level)));
        }
      }
    }

    // Get the corresponding on/off value depending on the configured values
    private State parseDigitalUpdate(int value, Integer onValue,
        Integer offValue, State onState, State offState) {
      if (onValue != null && offValue != null) {
        // if on and off configured,
        // if either match => use state
        // otherwise => UNDEF
        if (value == onValue) {
          return onState;
        } else if (value == offValue) {
          return offState;
        } else {
          return UnDefType.UNDEF;
        }
      } else if (onValue != null) {
        // if only on configured,
        // if on matches => ON
        // otherwise => OFF
        if (value == onValue) {
          return onState;
        } else {
          return offState;
        }
      } else if (offValue != null) {
        // if only off configured,
        // if off matches => OFF
        // otherwise => ON
        if (value == offValue) {
          return offState;
        } else {
          return onState;
        }
      } else {
        // if neither configured,
        // if 0 => OFF
        // otherwise => ON
        if (value == 0) {
          return offState;
        } else {
          return onState;
        }
      }
    }

    private void sendUpdate(InsteonHubBindingConfig config, State update) {
      eventPublisher.postUpdate(config.getItemName(), update);
    }
  }

  private final Thread dimStopThread = new Thread() {
    @Override
    public void run() {
      while (activated) {
        long curTime = System.currentTimeMillis();
        synchronized (itemDimTimeouts) {
          // check all timeouts
          for (Map.Entry<String, AtomicLong> entry : itemDimTimeouts
              .entrySet()) {
            // parse from entry
            String itemName = entry.getKey();
            AtomicLong timeout = entry.getValue();
            // check if timeout is set and has elapsed
            if (timeout.get() > 0 && curTime > timeout.get()) {
              // timeout elapsed => reset timeout and stop dim/brt
              timeout.set(0);
              InsteonHubBindingConfig config = InsteonHubBindingConfigUtil
                  .getConfigForItem(providers, itemName);
              InsteonHubProxy proxy = proxies.get(config
                  .getDeviceInfo().getHubId());
              proxy.stopDeviceAdjustment(config.getDeviceInfo()
                  .getDeviceId());
            }
          }
        }
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          // ignore
        }
      }
    }
  };
}
TOP

Related Classes of org.openhab.binding.insteonhub.internal.InsteonHubBinding$AsyncEventPublisher

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.