/** * 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.maxcul.internal; 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.SwitchItem; import org.openhab.model.item.binding.BindingConfigParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This function parses the Binding Configuration for Max! CUL bindings and * populates the configuration object * * TODO implement tests to validate this parser * * @author Paul Hampson (cyclingengineer) * @since 1.6.0 */ public class MaxCulBindingConfigParser { private static final Logger logger = LoggerFactory.getLogger(MaxCulBindingConfigParser.class); public static void parseMaxCulBindingString(String bindingConfigStr, MaxCulBindingConfig cfg) throws BindingConfigParseException { String[] configParts = bindingConfigStr.trim().split(":"); if (bindingConfigStr.startsWith("PairMode")) { logger.debug("Pair Mode switch found"); cfg.setDeviceType(MaxCulDevice.PAIR_MODE); return; } else if (bindingConfigStr.startsWith("ListenMode")) { logger.debug("Listen Mode switch found"); cfg.setDeviceType(MaxCulDevice.LISTEN_MODE); return; } else if (bindingConfigStr.startsWith("CreditMonitor")) { logger.debug("Credit Monitor binding found"); cfg.setDeviceType(MaxCulDevice.CREDIT_MONITOR); } else if (bindingConfigStr.startsWith("LedMode")) { logger.debug("LED Mode binding found"); cfg.setDeviceType(MaxCulDevice.LED_MODE); } else if (configParts.length < 2) { throw new BindingConfigParseException( "MaxCul configuration requires a configuration of at least the format <device_type>:<serial_num> for a MAX! device."); } else { logger.debug("Found real device"); /* handle device type */ logger.debug("Part 0/" + (configParts.length - 1) + " -> " + configParts[0]); parseDeviceCategory(configParts[0], cfg); /* handle serial number */ logger.debug("Part 1/" + (configParts.length - 1) + " -> " + configParts[1]); cfg.setSerialNumber(configParts[1]); /* handle optional config items */ if (configParts.length > 2) { // parts 3 onwards for (int idx = 2; idx < configParts.length; idx++) { logger.debug("Part " + idx + "/" + (configParts.length - 1) + " -> " + configParts[idx]); if (configParts[idx].startsWith("configTemp")) { parseConfigTemp(configParts[idx], cfg); } else if (configParts[idx].startsWith("associate")) { parseAssociation(configParts[idx], cfg); } else if (configParts[idx].startsWith("feature")) { parseDeviceFeature(configParts[idx], cfg); } } } else { /* use defaults - handle all device types */ switch (cfg.getDeviceType()) { case PUSH_BUTTON: cfg.setFeature(MaxCulFeature.SWITCH); break; case RADIATOR_THERMOSTAT: cfg.setFeature(MaxCulFeature.THERMOSTAT); break; case RADIATOR_THERMOSTAT_PLUS: cfg.setFeature(MaxCulFeature.THERMOSTAT); break; case SHUTTER_CONTACT: cfg.setFeature(MaxCulFeature.CONTACT); break; case WALL_THERMOSTAT: cfg.setFeature(MaxCulFeature.THERMOSTAT); break; case CREDIT_MONITOR: case PAIR_MODE: case LISTEN_MODE: case LED_MODE: case CUBE: case UNKNOWN: break; } } /* * load stored configuration from pairing (if present) except on * pair/listen mode/credit monitor bindings */ if (cfg.getDeviceType() != MaxCulDevice.PAIR_MODE || cfg.getDeviceType() != MaxCulDevice.LISTEN_MODE || cfg.getDeviceType() != MaxCulDevice.CREDIT_MONITOR) { cfg.loadStoredConfig(); } } } private static void parseDeviceCategory(String configPart, MaxCulBindingConfig cfg) throws BindingConfigParseException { cfg.setDeviceType(MaxCulDevice.UNKNOWN); if (configPart.equals("RadiatorThermostat")) { cfg.setDeviceType(MaxCulDevice.RADIATOR_THERMOSTAT); } else if (configPart.equals("RadiatorThermostatPlus")) { cfg.setDeviceType(MaxCulDevice.RADIATOR_THERMOSTAT_PLUS); } else if (configPart.equals("WallThermostat")) { cfg.setDeviceType(MaxCulDevice.WALL_THERMOSTAT); } else if (configPart.equals("PushButton")) { cfg.setDeviceType(MaxCulDevice.PUSH_BUTTON); } else if (configPart.equals("ShutterContact")) { cfg.setDeviceType(MaxCulDevice.SHUTTER_CONTACT); } else { throw new BindingConfigParseException( "Invalid device type. Use RadiatorThermostat / RadiatorThermostatPlus / WallThermostat / PushButton / ShutterContact"); } } private static void parseDeviceFeature(String configParts, MaxCulBindingConfig cfg) throws BindingConfigParseException { String[] configPartArray = configParts.split("="); if (configPartArray.length == 2) { String configPart = configPartArray[1]; if (configPart.equals("thermostat")) { if (cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT && cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT_PLUS && cfg.getDeviceType() != MaxCulDevice.WALL_THERMOSTAT) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'thermostat' on radiator or wall thermostats. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.THERMOSTAT); } else if (configPart.equals("temperature")) { if (cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT && cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT_PLUS && cfg.getDeviceType() != MaxCulDevice.WALL_THERMOSTAT) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'temperature' on radiator or wall thermostats. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.TEMPERATURE); } else if (configPart.equals("battery")) { cfg.setFeature(MaxCulFeature.BATTERY); } else if (configPart.equals("displaySetting")) { if (cfg.getDeviceType() != MaxCulDevice.WALL_THERMOSTAT) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'displaySetting' on wall thermostats. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.DISPLAYSETTING); } else if (configPart.equals("mode")) { if (cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT && cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT_PLUS && cfg.getDeviceType() != MaxCulDevice.WALL_THERMOSTAT) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'mode' on radiator or wall thermostats. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.MODE); } else if (configPart.equals("switch")) { if (cfg.getDeviceType() != MaxCulDevice.PUSH_BUTTON) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'switch' on PushButton. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.SWITCH); } else if (configPart.equals("contact")) { if (cfg.getDeviceType() != MaxCulDevice.SHUTTER_CONTACT) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'contact' on ShutterContact. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.CONTACT); } else if (configPart.equals("valvepos")) { if (cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT && cfg.getDeviceType() != MaxCulDevice.RADIATOR_THERMOSTAT_PLUS) { throw new BindingConfigParseException( "Invalid device feature. Can only use 'switch' on RadiatorThermostat or RadiatorThermostatPlus. This is a " + cfg.getDeviceType()); } cfg.setFeature(MaxCulFeature.VALVE_POS); } else if (configPart.equals("reset")) { cfg.setFeature(MaxCulFeature.RESET); } } } private static void parseConfigTemp(String configPart, MaxCulBindingConfig cfg) throws BindingConfigParseException { String[] configKeyValueSplit = configPart.split("="); if (configKeyValueSplit.length == 2) { String[] configParts = configKeyValueSplit[1].split("/"); if (configParts.length == 7) { // <comfortTemp>/<ecoTemp>/<maxTemp>/<minTemp>/<windowOpenTemperature>/<windowOpenDuration>/<measurementOffset> cfg.setComfortTemp(Double.parseDouble(configParts[0])); cfg.setEcoTemp(Double.parseDouble(configParts[1])); cfg.setMaxTemp(Double.parseDouble(configParts[2])); cfg.setMinTemp(Double.parseDouble(configParts[3])); cfg.setWindowOpenTemperature(Double.parseDouble(configParts[4])); cfg.setWindowOpenDuration(Double.parseDouble(configParts[5])); cfg.setMeasurementOffset(Double.parseDouble(configParts[6])); cfg.setTemperatureConfigSet(true); } else { throw new BindingConfigParseException( "Temperature configuration should be of form 'configTemp=<comfortTemp>/<ecoTemp>/<maxTemp>/<minTemp>/<windowOpenTemperature>/<windowOpenDuration>/<measurementOffset>'"); } } else { throw new BindingConfigParseException( "Temperature configuration should be of form 'configTemp=<comfortTemp>/<ecoTemp>/<maxTemp>/<minTemp>/<windowOpenTemperature>/<windowOpenDuration>/<measurementOffset>'"); } } private static void parseAssociation(String configPart, MaxCulBindingConfig cfg) throws BindingConfigParseException { String[] configKeyValueSplit = configPart.split("="); if (configKeyValueSplit.length == 2) { String[] associations = configKeyValueSplit[1].split(","); cfg.clearAssociatedSerialNum(); for (int idx = 0; idx < associations.length; idx++) { cfg.addAssociatedSerialNum(associations[idx]); } } else { throw new BindingConfigParseException( "Format of association configuration is incorrect! must be 'association=<serialNum>[,<serialNum>,[...]]'"); } } /** * Validate if an item is of a valid type * * @param config * Populated configuration to check * @param item * Item to check * @throws BindingConfigParseException * Thrown when item type is invalid */ public static void validateItemType(MaxCulBindingConfig config, Item item) throws BindingConfigParseException { switch (config.getDeviceType()) { case PAIR_MODE: case LISTEN_MODE: case LED_MODE: if (!(item instanceof SwitchItem)) { throw new BindingConfigParseException( "Invalid item type. PairMode/ListenMode can only be a switch"); } else if (config.getFeature() == MaxCulFeature.RESET && !(item instanceof SwitchItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'reset' can only be a Switch"); } break; case PUSH_BUTTON: case SHUTTER_CONTACT: if (config.getFeature() == MaxCulFeature.BATTERY && !(item instanceof SwitchItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'battery' can only be a Switch"); } else if (config.getFeature() == MaxCulFeature.CONTACT && !(item instanceof ContactItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'contact' can only be a Contact"); } else if (config.getFeature() == MaxCulFeature.RESET && !(item instanceof SwitchItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'reset' can only be a Switch"); } break; case RADIATOR_THERMOSTAT: case RADIATOR_THERMOSTAT_PLUS: case WALL_THERMOSTAT: if (config.getFeature() == MaxCulFeature.TEMPERATURE && !(item instanceof NumberItem)) { throw new BindingConfigParseException( "Invalid item type. Feature 'temperature' can only be a Number"); } else if (config.getFeature() == MaxCulFeature.VALVE_POS && !(item instanceof NumberItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'valvepos' can only be a Number"); } else if (config.getFeature() == MaxCulFeature.THERMOSTAT && !((item instanceof NumberItem) || (item instanceof SwitchItem))) { throw new BindingConfigParseException( "Invalid item type. Feature 'thermostat' can only be a Number or a Switch"); } else if (config.getFeature() == MaxCulFeature.BATTERY && !(item instanceof SwitchItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'battery' can only be a Switch"); } else if (config.getFeature() == MaxCulFeature.MODE && !(item instanceof NumberItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'mode' can only be a Number"); } else if (config.getFeature() == MaxCulFeature.DISPLAYSETTING && !(item instanceof SwitchItem)) { throw new BindingConfigParseException( "Invalid item type. Feature 'displaysetting' can only be a Switch"); } else if (config.getFeature() == MaxCulFeature.RESET && !(item instanceof SwitchItem)) { throw new BindingConfigParseException("Invalid item type. Feature 'reset' can only be a Switch"); } break; default: throw new BindingConfigParseException( "Invalid config device type. Wasn't expecting " + config.getDeviceType()); } } }