Package org.openhab.binding.maxcube.internal

Source Code of org.openhab.binding.maxcube.internal.MaxCubeBinding

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

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.maxcube.MaxCubeBindingProvider;
import org.openhab.binding.maxcube.internal.message.C_Message;
import org.openhab.binding.maxcube.internal.message.Configuration;
import org.openhab.binding.maxcube.internal.message.Device;
import org.openhab.binding.maxcube.internal.message.DeviceInformation;
import org.openhab.binding.maxcube.internal.message.H_Message;
import org.openhab.binding.maxcube.internal.message.HeatingThermostat;
import org.openhab.binding.maxcube.internal.message.L_Message;
import org.openhab.binding.maxcube.internal.message.M_Message;
import org.openhab.binding.maxcube.internal.message.Message;
import org.openhab.binding.maxcube.internal.message.MessageType;
import org.openhab.binding.maxcube.internal.message.S_Command;
import org.openhab.binding.maxcube.internal.message.ShutterContact;
import org.openhab.binding.maxcube.internal.message.ThermostatModeType;
import org.openhab.binding.maxcube.internal.message.WallMountedThermostat;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The RefreshService polls the MAX!Cube frequently and updates the list of
* configurations and devices. The refresh interval can be changed via
* openhab.cfg.
*
* Note that the MAX Cube has a lock out that only allows a maximum of 36s of
* transmissions (1%) in total in 1 hour. This means that if too many S messages
* are sent then the cube no longer sends the data out.
*
* @author Andreas Heil (info@aheil.de)
* @since 1.4.0
*/
public class MaxCubeBinding extends AbstractActiveBinding<MaxCubeBindingProvider> implements ManagedService {

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

  /** The IP address of the MAX!Cube LAN gateway */
  private static String ip;

  /**
   * The port of the MAX!Cube LAN gateway as provided at
   * http://www.elv.de/controller.aspx?cid=824&detail=10&detail2=3484
   */
  private static int port = 62910;

  /** The refresh interval which is used to poll given MAX!Cube */
  private static long refreshInterval = 10000;

  /** MaxCube's default off temperature */
  private static final DecimalType DEFAULT_OFF_TEMPERATURE = new DecimalType(4.5);

  /** MaxCubes default on temperature */
  private static final DecimalType DEFAULT_ON_TEMPERATURE = new DecimalType(30.5);
 
  /**
   * Configuration and device lists, kept during the overall lifetime of the
   * binding
   */
  private ArrayList<Configuration> configurations = new ArrayList<Configuration>();
  private ArrayList<Device> devices = new ArrayList<Device>();

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

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

  /**
   * {@inheritDoc}
   */
  @Override
  public void activate() {
    super.activate();
    setProperlyConfigured(false);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void execute() {
    Socket socket = null;
    BufferedReader reader = null;

    if (ip == null) {
      logger.debug("Update prior to completion of interface IP configuration");
      return;
    }
    try {
      String raw = null;

      socket = new Socket(ip, port);
      reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

      boolean cont = true;
      while (cont) {
        raw = reader.readLine();
        if (raw == null) {
          cont = false;
          continue;
        }

        Message message;
        try {
          message = processRawMessage(raw);

          message.debug(logger);

          if (message != null) {
            if (message.getType() == MessageType.M) {
              M_Message msg = (M_Message) message;
              for (DeviceInformation di : msg.devices) {
                Configuration c = null;
                for (Configuration conf : configurations) {
                  if (conf.getSerialNumber().equalsIgnoreCase(di.getSerialNumber())) {
                    c = conf;
                    break;
                  }
                }

                if (c != null) {
                  configurations.remove(c);
                }

                c = Configuration.create(di);
                configurations.add(c);

                c.setRoomId(di.getRoomId());
              }
            } else if (message.getType() == MessageType.C) {
              Configuration c = null;
              for (Configuration conf : configurations) {
                if (conf.getSerialNumber().equalsIgnoreCase(((C_Message) message).getSerialNumber())) {
                  c = conf;
                  break;
                }
              }

              if (c == null) {
                configurations.add(Configuration.create(message));
              } else {
                c.setValues((C_Message) message);
              }
            } else if (message.getType() == MessageType.L) {
              Collection<? extends Device> tempDevices = ((L_Message) message).getDevices(configurations);

              for (Device d : tempDevices) {
                Device existingDevice = findDevice(d.getSerialNumber(), devices);
                if (existingDevice == null) {
                  devices.add(d);
                } else {
                  devices.remove(existingDevice);
                  devices.add(d);
                }
              }

              logger.debug("{} devices found.", devices.size());

              // the L message is the last one, while the reader
              // would hang trying to read a new line and
              // eventually the
              // cube will fail to establish
              // new connections for some time
              cont = false;
            }
          }
        } catch (Exception e) {
          logger.info("Failed to process message received by MAX! protocol.");
          logger.debug(Utils.getStackTrace(e));
        }
      }

      socket.close();

      for (MaxCubeBindingProvider provider : providers) {
        for (String itemName : provider.getItemNames()) {
          String serialNumber = provider.getSerialNumber(itemName);

          Device device = findDevice(serialNumber, devices);

          if (device == null) {
            logger.info("Cannot find MAX!cube device with serial number '{}'", serialNumber);

            if (logger.isDebugEnabled()) {
              StringBuilder sb = new StringBuilder();
              sb.append("Available MAX! devices are:");
              for (Device d : devices) {
                sb.append("\n\t");
                sb.append(d.getSerialNumber());
              }
              logger.debug(sb.toString());
            }
            continue;
          }

          switch (device.getType()) {
          case HeatingThermostatPlus:
          case HeatingThermostat:
            if (provider.getBindingType(itemName) == BindingType.VALVE) {
              eventPublisher.postUpdate(itemName, ((HeatingThermostat) device).getValvePosition());
            } else if (provider.getBindingType(itemName) == BindingType.MODE) {
              eventPublisher.postUpdate(itemName, ((HeatingThermostat) device).getModeString());
            } else if (provider.getBindingType(itemName) == BindingType.BATTERY) {
              eventPublisher.postUpdate(itemName, ((HeatingThermostat) device).getBatteryLow());
            } else if (provider.getBindingType(itemName) == BindingType.ACTUAL) {
              eventPublisher.postUpdate(itemName, ((HeatingThermostat) device).getTemperatureActual());
            } else {
              eventPublisher.postUpdate(itemName, ((HeatingThermostat) device).getTemperatureSetpoint());
            }
            break;
          case ShutterContact:
            if (provider.getBindingType(itemName) == BindingType.BATTERY) {
              eventPublisher.postUpdate(itemName, ((ShutterContact) device).getBatteryLow());
            } else {
              eventPublisher.postUpdate(itemName, ((ShutterContact) device).getShutterState());
            }
            break;
          case WallMountedThermostat:
            if (provider.getBindingType(itemName) == BindingType.ACTUAL) {
              eventPublisher.postUpdate(itemName, ((WallMountedThermostat) device).getTemperatureActual());
            } else if (provider.getBindingType(itemName) == BindingType.MODE) {
              eventPublisher.postUpdate(itemName, ((WallMountedThermostat) device).getModeString());
            } else if (provider.getBindingType(itemName) == BindingType.BATTERY) {
              eventPublisher.postUpdate(itemName, ((WallMountedThermostat) device).getBatteryLow());
            } else {
            eventPublisher.postUpdate(itemName, ((WallMountedThermostat) device).getTemperatureSetpoint());
            }
            break;
          default:
            // no further devices supported yet
          }
        }
      }
    } catch (UnknownHostException e) {
      logger.info("Cannot establish connection with MAX!Cube lan gateway while connecting to '{}'", ip);
      logger.debug(Utils.getStackTrace(e));
    } catch (IOException e) {
      logger.info("Cannot read data from MAX!Cube lan gateway while connecting to '{}'", ip);
      logger.debug(Utils.getStackTrace(e));
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void internalReceiveCommand(String itemName, Command command) {
    logger.debug("Received command from {}", itemName);

    // resolve serial number for item
    String serialNumber = null;

    for (MaxCubeBindingProvider provider : providers) {
      serialNumber = provider.getSerialNumber(itemName);

      if (serialNumber.equals(null)) {
        continue;
      }

      // send command to MAX!Cube LAN Gateway
      Device device = findDevice(serialNumber, devices);

      if (device == null) {
        logger.debug("Cannot send command to device with serial number {}, device not listed.", serialNumber);
        continue;
      }

      String rfAddress = device.getRFAddress();
      String commandString = null;

      if (command instanceof DecimalType || command instanceof OnOffType) {
        DecimalType decimalType = DEFAULT_OFF_TEMPERATURE;
        if (command instanceof DecimalType) {
          decimalType = (DecimalType) command;
        } else if (command instanceof OnOffType) {
          decimalType = OnOffType.ON.equals(command) ? DEFAULT_ON_TEMPERATURE : DEFAULT_OFF_TEMPERATURE;
        }

        S_Command cmd = new S_Command(rfAddress, device.getRoomId(), decimalType.doubleValue());
        commandString = cmd.getCommandString();
      } else if (command instanceof StringType) {
        String commandContent = command.toString().trim().toUpperCase();
        ThermostatModeType commandThermoType = null;
        if (commandContent.contentEquals(ThermostatModeType.AUTOMATIC.toString())) {
          commandThermoType = ThermostatModeType.AUTOMATIC;
        } else if (commandContent.contentEquals(ThermostatModeType.BOOST.toString())) {
          commandThermoType = ThermostatModeType.BOOST;
        } else {
          logger.debug("Only updates to AUTOMATIC & BOOST supported, received value ;'{}'", commandContent);
          continue;
        }

        S_Command cmd = new S_Command(rfAddress, device.getRoomId(), commandThermoType);
        commandString = cmd.getCommandString();
      }

      if (commandString != null) {
        Socket socket = null;
        try {
          socket = new Socket(ip, port);
          DataOutputStream stream = new DataOutputStream(socket.getOutputStream());

          byte[] b = commandString.getBytes();
          stream.write(b);
          socket.close();

        } catch (UnknownHostException e) {
          logger.warn("Cannot establish connection with MAX!cube lan gateway while sending command to '{}'", ip);
          logger.debug(Utils.getStackTrace(e));
        } catch (IOException e) {
          logger.warn("Cannot write data from MAX!Cube lan gateway while connecting to '{}'", ip);
          logger.debug(Utils.getStackTrace(e));
        }
        logger.debug("Command Sent to {}", ip);
      } else {
        logger.debug("Null Command not sent to {}", ip);
      }
    }
  }

  private Device findDevice(String serialNumber, ArrayList<Device> devices) {
    for (Device device : devices) {
      if (device.getSerialNumber().toUpperCase().equals(serialNumber)) {
        return device;
      }
    }
    return null;
  }

  /**
   * Processes the raw TCP data read from the MAX protocol, returning the
   * corresponding Message.
   *
   * @param raw
   *            the raw data provided read from the MAX protocol
   * @return the correct message for the given raw data
   */
  private Message processRawMessage(String raw) {

    if (raw.startsWith("H:")) {
      return new H_Message(raw);
    } else if (raw.startsWith("M:")) {
      return new M_Message(raw);
    } else if (raw.startsWith("C:")) {
      return new C_Message(raw);
    } else if (raw.startsWith("L:")) {
      return new L_Message(raw);
    } else {
      logger.debug("Unknown message block: '{}'",raw);
    }
      return null;
  }

  /**
   * {@inheritDoc}
   */
  @SuppressWarnings("rawtypes")
  public void updated(Dictionary config) throws ConfigurationException {
    if (config != null) {
      ip = (String) config.get("ip");
      if (StringUtils.isBlank(ip)) {
        ip = discoveryGatewayIp();
      }

      String portString = (String) config.get("port");
      if (portString != null && !portString.isEmpty()) {
        if (port > 0 && port <= 65535) {
          port = Integer.parseInt(portString);
        }
      }

      String refreshIntervalString = (String) config.get("refreshInterval");
      if (refreshIntervalString != null && !refreshIntervalString.isEmpty()) {
        refreshInterval = Long.parseLong(refreshIntervalString);
      }
    } else {
      ip = discoveryGatewayIp();
    }
   
    setProperlyConfigured(ip != null);
  }

  /**
   * Discovers the MAX!CUbe LAN Gateway IP address.
   *
   * @return the cube IP if available, a blank string otherwise.
   * @throws ConfigurationException
   */
  private String discoveryGatewayIp() throws ConfigurationException {
    String ip = MaxCubeDiscover.discoverIp();
    if (ip == null) {
      throw new ConfigurationException("maxcube:ip", "IP address for MAX!Cube must be set");
    } else {
      logger.info("Discovered MAX!Cube lan gateway at '{}'", ip);
    }
    return ip;
  }
}
TOP

Related Classes of org.openhab.binding.maxcube.internal.MaxCubeBinding

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.