Package org.openhab.binding.maxcul.internal

Source Code of org.openhab.binding.maxcul.internal.MaxCulBinding

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

import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.maxcul.MaxCulBindingProvider;
import org.openhab.binding.maxcul.internal.message.sequencers.PairingInitialisationSequence;
import org.openhab.binding.maxcul.internal.message.sequencers.TimeUpdateRequestSequence;
import org.openhab.binding.maxcul.internal.messages.BaseMsg;
import org.openhab.binding.maxcul.internal.messages.MaxCulBindingMessageProcessor;
import org.openhab.binding.maxcul.internal.messages.MaxCulMsgType;
import org.openhab.binding.maxcul.internal.messages.PairPingMsg;
import org.openhab.binding.maxcul.internal.messages.PushButtonMsg;
import org.openhab.binding.maxcul.internal.messages.PushButtonMsg.PushButtonMode;
import org.openhab.binding.maxcul.internal.messages.SetTemperatureMsg;
import org.openhab.binding.maxcul.internal.messages.ThermostatStateMsg;
import org.openhab.binding.maxcul.internal.messages.TimeInfoMsg;
import org.openhab.binding.maxcul.internal.messages.WallThermostatControlMsg;
import org.openhab.binding.maxcul.internal.messages.WallThermostatStateMsg;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.Command;
import org.openhab.io.transport.cul.CULDeviceException;
import org.openhab.io.transport.cul.CULHandler;
import org.openhab.io.transport.cul.CULManager;
import org.openhab.io.transport.cul.CULMode;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This binding allows integration of the MAX! devices via the CUL device - so
* without the need for the Max!Cube device.
*
* @author Paul Hampson (cyclingengineer)
* @since 1.6.0
*/
public class MaxCulBinding extends AbstractBinding<MaxCulBindingProvider>
    implements ManagedService, MaxCulBindingMessageProcessor {

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

  /**
   * The device that is used to access the CUL hardware
   */
  private String accessDevice;

  /**
   * This provides access to the CULFW device (e.g. USB stick)
   */
  private CULHandler cul;

  /**
   * This sets the address of the controller i.e. us!
   */
  private final String srcAddr = "010203";

  /**
   * Set default group ID
   */
  private final byte DEFAULT_GROUP_ID = 0x1;

  /**
   * Flag to indicate if we are in pairing mode. Default timeout is 60
   * seconds.
   */
  private boolean pairMode = false;
  private int pairModeTimeout = 60000;
  private int PACED_TRANSMIT_TIME = 10000;

  private Map<String, Timer> timers = new HashMap<String, Timer>();
  private Map<MaxCulBindingConfig, Timer> pacedBindingTransmitTimers = new HashMap<MaxCulBindingConfig, Timer>();

  MaxCulMsgHandler messageHandler;

  private String tzStr;

  public MaxCulBinding() {
  }

  public void activate() {
    super.activate();
    logger.debug("Activating MaxCul binding");
  }

  public void deactivate() {
    logger.debug("De-Activating MaxCul binding");
    if (cul != null) {
      cul.unregisterListener(messageHandler);
      CULManager.close(cul);
      logger.debug("CUL IO should now be closed");
    }
  }

  /**
   * @{inheritDoc
   */
  @Override
  protected void internalReceiveCommand(final String itemName, Command command) {
    Timer pairModeTimer = null;

    MaxCulBindingConfig bindingConfig = null;
    for (MaxCulBindingProvider provider : super.providers) {
      bindingConfig = provider.getConfigForItemName(itemName);
      if (bindingConfig != null) {
        break;
      }
    }
    logger.debug("Received command " + command.toString() + " for item "
        + itemName);
    if (bindingConfig != null) {
      logger.debug("Found config for " + itemName);

      switch (bindingConfig.getDeviceType()) {
      case PAIR_MODE:
        if ((command instanceof OnOffType)) {
          switch ((OnOffType) command) {
          case ON:
            /*
             * turn on pair mode and schedule disabling of pairing
             * mode
             */
            pairMode = true;
            TimerTask task = new TimerTask() {
              public void run() {
                logger.debug(itemName
                    + " pairMode time out executed");
                pairMode = false;
                eventPublisher.postUpdate(itemName,
                    OnOffType.OFF);
              }
            };
            pairModeTimer = timers.get(itemName);
            if (pairModeTimer != null) {
              pairModeTimer.cancel();
              timers.remove(itemName);
            }
            pairModeTimer = new Timer();
            timers.put(itemName, pairModeTimer);
            pairModeTimer.schedule(task, pairModeTimeout);
            logger.debug(itemName
                + " pairMode enabled & timeout scheduled");
            break;
          case OFF:
            /*
             * we are manually disabling, so clear the timer and the
             * flag
             */
            pairMode = false;
            pairModeTimer = timers.get(itemName);
            if (pairModeTimer != null) {
              logger.debug(itemName + " pairMode timer cancelled");
              pairModeTimer.cancel();
              timers.remove(itemName);
            }
            logger.debug(itemName + " pairMode cleared");
            break;
          }
        } else
          logger.warn("Command not handled for "
              + bindingConfig.getDeviceType()
              + " that is not OnOffType");
        break;
      case LISTEN_MODE:
        if (command instanceof OnOffType) {
          this.messageHandler
              .setListenMode(((OnOffType) command == OnOffType.ON));
        } else
          logger.warn("Command not handled for "
              + bindingConfig.getDeviceType()
              + " that is not OnOffType");
        break;
      case RADIATOR_THERMOSTAT:
      case RADIATOR_THERMOSTAT_PLUS:
      case WALL_THERMOSTAT:
        if (bindingConfig.getFeature() == MaxCulFeature.THERMOSTAT) {
          /* clear out old pacing timer */
          if (pacedBindingTransmitTimers.containsKey(bindingConfig)) {
            pacedBindingTransmitTimers.get(bindingConfig).cancel();
            pacedBindingTransmitTimers.remove(bindingConfig);
          }
          /* schedule new timer */
          Timer pacingTimer = new Timer();
          pacedBindingTransmitTimers.put(bindingConfig, pacingTimer);
          pacingTimer.schedule(new MaxCulPacedThermostatTransmitTask(
              command, bindingConfig, messageHandler,
              super.providers), PACED_TRANSMIT_TIME);
        } else if (bindingConfig.getFeature() == MaxCulFeature.RESET) {
          messageHandler.sendReset(bindingConfig.getDevAddr());
        } else {
          logger.warn("Command not handled for "
              + bindingConfig.getDeviceType()
              + " that is not OnOffType or DecimalType");
        }
        break;
      default:
        logger.warn("Command not handled for "
            + bindingConfig.getDeviceType());
        break;
      }
    }
    updateCreditMonitors();
  }

  /**
   * @{inheritDoc
   */
  @Override
  public void updated(Dictionary<String, ?> config)
      throws ConfigurationException {
    logger.debug("MaxCUL Reading config");
    if (config != null) {

      // handle timezone configuration
      // maxcul:timezone=Europe/London
      String timezoneString = (String) config.get("timezone");
      if (StringUtils.isNotBlank(timezoneString)) {
        this.tzStr = timezoneString;
      } else {
        this.tzStr = "Europe/London";
      }

      // handle device config
      // maxcul:device=/dev/cul
      String deviceString = (String) config.get("device");
      if (StringUtils.isNotBlank(deviceString)) {
        logger.debug("Setting up device " + deviceString);
        setupDevice(deviceString);
        if (cul == null)
          throw new ConfigurationException("device",
              "Configuration failed. Unable to access CUL device "
                  + deviceString);
      } else {
        throw new ConfigurationException("device",
            "No device set - please set one");
      }
    }
  }

  private void setupDevice(String device) {
    if (cul != null) {
      CULManager.close(cul);
    }
    try {
      accessDevice = device;
      logger.debug("Opening CUL device on " + accessDevice);
      cul = CULManager.getOpenCULHandler(accessDevice, CULMode.MAX);
      messageHandler = new MaxCulMsgHandler(this.srcAddr, cul,
          super.providers);
      messageHandler.registerMaxCulBindingMessageProcessor(this);
    } catch (CULDeviceException e) {
      logger.error("Cannot open CUL device", e);
      cul = null;
      accessDevice = null;
    }
  }

  private Collection<MaxCulBindingConfig> getBindingsBySerial(String serial) {
    Collection<MaxCulBindingConfig> bindingConfigs = null;
    for (MaxCulBindingProvider provider : super.providers) {
      bindingConfigs = provider.getConfigsForSerialNumber(serial);
      if (bindingConfigs != null)
        break;
    }
    if (bindingConfigs == null) {
      logger.error("Unable to find configuration for serial " + serial
          + ". Do you have a binding for it?");
      return null;
    }
    return bindingConfigs;
  }

  private void updateCreditMonitors()
  {
    /* find and update credit monitor binding if it exists */
    int credit10ms = messageHandler.getCreditStatus();
    for (MaxCulBindingProvider provider : super.providers) {
      Collection<MaxCulBindingConfig> bindingConfigs = provider
          .getCreditMonitorBindings();
      for (MaxCulBindingConfig bc : bindingConfigs) {
        String itemName = provider.getItemNameForConfig(bc);
        eventPublisher
            .postUpdate(itemName, new DecimalType(credit10ms));
      }
    }
  }
 
  @Override
  public void maxCulMsgReceived(String data, boolean isBroadcast) {
    logger.debug("Received data from CUL: " + data);

    MaxCulMsgType msgType = BaseMsg.getMsgType(data);
    /*
     * Check if it's broadcast and we're in pair mode or a PAIR_PING message
     * directly for us
     */
    if (((pairMode && isBroadcast) || !isBroadcast)
        && msgType == MaxCulMsgType.PAIR_PING) {
      logger.debug("Got PAIR_PING message");
      MaxCulBindingConfig configWithTempsConfig = null;
      /* process packet */
      PairPingMsg pkt = new PairPingMsg(data);

      /* Match serial number to binding configuration */
      Collection<MaxCulBindingConfig> bindingConfigs = getBindingsBySerial(pkt.serial);

      /*
       * only respond and set pairing info if we found at least one
       * binding config
       */
      if (bindingConfigs != null) {
        logger.debug("Found " + bindingConfigs.size() + " configs for "
            + pkt.serial);
        for (MaxCulBindingConfig bc : bindingConfigs) {
          /* Set pairing information */
          bc.setPairedInfo(pkt.srcAddrStr); /*
                           * where it came from gives
                           * the addr of the device
                           */
          if (bc.isTemperatureConfigSet()
              && configWithTempsConfig == null) {
            configWithTempsConfig = bc;
          }
        }

        /* if none have values set then send default from first config */
        if (configWithTempsConfig == null) {
          logger.debug("Using default temperature configuration from config 0");
          configWithTempsConfig = (MaxCulBindingConfig) bindingConfigs
              .toArray()[0];
        }

        /* get device associations */
        HashSet<MaxCulBindingConfig> associations = null;
        for (MaxCulBindingProvider provider : super.providers) {
          associations = provider
              .getAssociations(configWithTempsConfig
                  .getSerialNumber());
          if (associations != null && associations.isEmpty() == false) {
            logger.debug("Found associations");
            break;
          }
        }

        /* start the initialisation sequence */
        logger.debug("Creating pairing sequencer");
        PairingInitialisationSequence ps = new PairingInitialisationSequence(
            this.DEFAULT_GROUP_ID, messageHandler,
            configWithTempsConfig, associations);
        messageHandler.startSequence(ps, pkt);
      } else {
        logger.error("Pairing failed: Unable to find binding config for device "
            + pkt.serial);
      }
    } else {
      switch (msgType) {
      /*
       * TODO there are other incoming messages that aren't handled that
       * could be
       */
      case WALL_THERMOSTAT_CONTROL:
        WallThermostatControlMsg wallThermCtrlMsg = new WallThermostatControlMsg(
            data);
        wallThermCtrlMsg.printMessage();
        for (MaxCulBindingProvider provider : super.providers) {
          Collection<MaxCulBindingConfig> bindingConfigs = provider
              .getConfigsForRadioAddr(wallThermCtrlMsg.srcAddrStr);
          for (MaxCulBindingConfig bc : bindingConfigs) {
            if (bc.getFeature() == MaxCulFeature.THERMOSTAT
                && wallThermCtrlMsg.getDesiredTemperature() != null) {
              String itemName = provider.getItemNameForConfig(bc);
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(wallThermCtrlMsg
                      .getDesiredTemperature()));
            } else if (bc.getFeature() == MaxCulFeature.TEMPERATURE
                && wallThermCtrlMsg.getMeasuredTemperature() != null) {
              String itemName = provider.getItemNameForConfig(bc);
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(wallThermCtrlMsg
                      .getMeasuredTemperature()));
            }
            // TODO switch mode between manual/automatic?
          }
        }

        /* reply only if not broadcast */
        if (isBroadcast == false)
          this.messageHandler.sendAck(wallThermCtrlMsg);
        break;
      case SET_TEMPERATURE:
        SetTemperatureMsg setTempMsg = new SetTemperatureMsg(data);
        setTempMsg.printMessage();
        for (MaxCulBindingProvider provider : super.providers) {
          Collection<MaxCulBindingConfig> bindingConfigs = provider
              .getConfigsForRadioAddr(setTempMsg.srcAddrStr);
          for (MaxCulBindingConfig bc : bindingConfigs) {
            if (bc.getFeature() == MaxCulFeature.THERMOSTAT) {
              String itemName = provider.getItemNameForConfig(bc);
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(setTempMsg
                      .getDesiredTemperature()));
            }
            // TODO switch mode between manual/automatic?
          }
        }
        /* respond to device */
        if (isBroadcast == false)
          this.messageHandler.sendAck(setTempMsg);
        break;
      case THERMOSTAT_STATE:
        ThermostatStateMsg thermStateMsg = new ThermostatStateMsg(data);
        thermStateMsg.printMessage();
        for (MaxCulBindingProvider provider : super.providers) {
          Collection<MaxCulBindingConfig> bindingConfigs = provider
              .getConfigsForRadioAddr(thermStateMsg.srcAddrStr);
          for (MaxCulBindingConfig bc : bindingConfigs) {
            String itemName = provider.getItemNameForConfig(bc);
            if (bc.getFeature() == MaxCulFeature.THERMOSTAT
                && thermStateMsg.getDesiredTemperature() != null) {
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(thermStateMsg
                      .getDesiredTemperature()));
            } else if (bc.getFeature() == MaxCulFeature.TEMPERATURE
                && thermStateMsg.getMeasuredTemperature() != null) {
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(thermStateMsg
                      .getMeasuredTemperature()));
            } else if (bc.getFeature() == MaxCulFeature.BATTERY) {
              eventPublisher.postUpdate(itemName, thermStateMsg
                  .getBatteryLow() ? OnOffType.ON
                  : OnOffType.OFF);
            } else if (bc.getFeature() == MaxCulFeature.MODE) {
              eventPublisher.postUpdate(itemName,
                  new DecimalType(thermStateMsg
                      .getControlMode().toInt()));
            } else if (bc.getFeature() == MaxCulFeature.VALVE_POS) {
              eventPublisher
                  .postUpdate(itemName, new DecimalType(
                      thermStateMsg.getValvePos()));
            }
            // TODO switch mode between manual/automatic?
          }
        }
        /* respond to device */
        if (isBroadcast == false)
          this.messageHandler.sendAck(thermStateMsg);
        break;
      case WALL_THERMOSTAT_STATE:
        WallThermostatStateMsg wallThermStateMsg = new WallThermostatStateMsg(
            data);
        wallThermStateMsg.printMessage();
        for (MaxCulBindingProvider provider : super.providers) {
          Collection<MaxCulBindingConfig> bindingConfigs = provider
              .getConfigsForRadioAddr(wallThermStateMsg.srcAddrStr);
          for (MaxCulBindingConfig bc : bindingConfigs) {
            String itemName = provider.getItemNameForConfig(bc);
            if (bc.getFeature() == MaxCulFeature.THERMOSTAT
                && wallThermStateMsg.getDesiredTemperature() != null) {
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(wallThermStateMsg
                      .getDesiredTemperature()));
            } else if (bc.getFeature() == MaxCulFeature.TEMPERATURE
                && wallThermStateMsg.getMeasuredTemperature() != null) {
              eventPublisher.postUpdate(
                  itemName,
                  new DecimalType(wallThermStateMsg
                      .getMeasuredTemperature()));
            } else if (bc.getFeature() == MaxCulFeature.BATTERY) {
              eventPublisher
                  .postUpdate(itemName, wallThermStateMsg
                      .getBatteryLow() ? OnOffType.ON
                      : OnOffType.OFF);
            } else if (bc.getFeature() == MaxCulFeature.MODE) {
              eventPublisher.postUpdate(itemName,
                  new DecimalType(wallThermStateMsg
                      .getControlMode().toInt()));
            }
          }
        }
        /* respond to device */
        if (isBroadcast == false)
          this.messageHandler.sendAck(wallThermStateMsg);
        break;
      case TIME_INFO:
        TimeInfoMsg timeMsg = new TimeInfoMsg(data);
        timeMsg.printMessage();
        TimeUpdateRequestSequence timeSeq = new TimeUpdateRequestSequence(
            this.tzStr, messageHandler);
        messageHandler.startSequence(timeSeq, timeMsg);
        break;
      case PUSH_BUTTON_STATE:
        PushButtonMsg pbMsg = new PushButtonMsg(data);
        pbMsg.printMessage();
        for (MaxCulBindingProvider provider : super.providers) {
          Collection<MaxCulBindingConfig> bindingConfigs = provider
              .getConfigsForRadioAddr(pbMsg.srcAddrStr);
          for (MaxCulBindingConfig bc : bindingConfigs) {
            String itemName = provider.getItemNameForConfig(bc);
            if (bc.getFeature() == MaxCulFeature.SWITCH) {
              // ON maps to 'AUTO'
              if (pbMsg.getMode() == PushButtonMode.AUTO)
                eventPublisher.postUpdate(itemName,
                    OnOffType.ON);
              // OFF maps to 'ECO'
              else if (pbMsg.getMode() == PushButtonMode.ECO)
                eventPublisher.postUpdate(itemName,
                    OnOffType.OFF);
            }
          }
        }
        if (isBroadcast == false)
          this.messageHandler.sendAck(pbMsg);
        break;
      default:
        logger.debug("Unhandled message type " + msgType.toString());
        break;

      }
    }
    updateCreditMonitors();
  }
}
TOP

Related Classes of org.openhab.binding.maxcul.internal.MaxCulBinding

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.