org.openhab.binding.homematic.internal.communicator.client.TclRegaScriptClient.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.homematic.internal.communicator.client.TclRegaScriptClient.java

Source

/**
 * 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.homematic.internal.communicator.client;

import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.homematic.internal.common.HomematicContext;
import org.openhab.binding.homematic.internal.communicator.RemoteControlOptionParser;
import org.openhab.binding.homematic.internal.config.binding.DatapointConfig;
import org.openhab.binding.homematic.internal.config.binding.HomematicBindingConfig;
import org.openhab.binding.homematic.internal.config.binding.VariableConfig;
import org.openhab.binding.homematic.internal.model.CommonUnmarshallerListener;
import org.openhab.binding.homematic.internal.model.HmChannel;
import org.openhab.binding.homematic.internal.model.HmDatapoint;
import org.openhab.binding.homematic.internal.model.HmDevice;
import org.openhab.binding.homematic.internal.model.HmDeviceList;
import org.openhab.binding.homematic.internal.model.HmProgram;
import org.openhab.binding.homematic.internal.model.HmProgramList;
import org.openhab.binding.homematic.internal.model.HmRemoteControlOptions;
import org.openhab.binding.homematic.internal.model.HmResult;
import org.openhab.binding.homematic.internal.model.HmValueItem;
import org.openhab.binding.homematic.internal.model.HmVariable;
import org.openhab.binding.homematic.internal.model.HmVariableList;
import org.openhab.binding.homematic.internal.model.TclScript;
import org.openhab.binding.homematic.internal.model.TclScripts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A client to execute TclRega scripts on the CCU. It's mainly used for
 * metadata/value retrieval, getting and setting variables and executing
 * programs.
 * 
 * @author Gerhard Riegler
 * @since 1.5.0
 */
public class TclRegaScriptClient {
    private static final Logger logger = LoggerFactory.getLogger(TclRegaScriptClient.class);
    private static final boolean TRACE_ENABLED = logger.isTraceEnabled();

    private Map<String, String> tclregaScripts;
    private HttpClient httpClient;

    private HomematicContext context;

    /**
     * Creates the TclRegaScriptClient.
     */
    public TclRegaScriptClient(HomematicContext context) {
        this.context = context;
    }

    /**
     * Starts the TclRegaScriptClient.
     */
    public void start() throws CcuClientException {
        logger.info("Starting {}", TclRegaScriptClient.class.getSimpleName());

        tclregaScripts = loadTclRegaScripts();

        httpClient = new HttpClient(new SimpleHttpConnectionManager(true));
        HttpClientParams params = httpClient.getParams();
        params.setConnectionManagerTimeout(5000);
        params.setSoTimeout(30000);
        params.setContentCharset("ISO-8859-1");
    }

    /**
     * Destroys the TclRegaScriptClient.
     */
    public void shutdown() {
        tclregaScripts = null;
        httpClient = null;
    }

    /**
     * Retrieves all variables from the CCU.
     */
    public void iterateAllVariables(TclIteratorCallback callback) throws CcuClientException {
        List<HmVariable> variables = sendScriptByName("getAllVariables", HmVariableList.class).getVariables();
        for (HmVariable variable : variables) {
            VariableConfig bindingConfig = new VariableConfig(variable.getName());
            callback.iterate(bindingConfig, variable);
        }
    }

    /**
     * Retrieves all datapoints from the CCU.
     */
    public void iterateAllDatapoints(TclIteratorCallback callback) throws CcuClientException {
        List<HmDevice> devices = sendScriptByName("getAllDevices", HmDeviceList.class).getDevices();
        for (HmDevice device : devices) {
            for (HmChannel channel : device.getChannels()) {
                for (HmDatapoint dp : channel.getDatapoints()) {
                    DatapointConfig bindingConfig = new DatapointConfig(device.getAddress(), channel.getNumber(),
                            dp.getName());
                    callback.iterate(bindingConfig, dp);
                }
            }
        }
    }

    /**
     * Retrieves all programs from the CCU.
     */
    public List<HmProgram> getAllPrograms() throws CcuClientException {
        return sendScriptByName("getAllPrograms", HmProgramList.class).getPrograms();
    }

    /**
     * Execute a program on the CCU.
     */
    public void executeProgram(String programName) throws CcuClientException {
        logger.debug("Executing program on CCU: {}", programName);
        HmResult result = sendScriptByName("executeProgram", HmResult.class, new String[] { "program_name" },
                new String[] { programName });
        if (!result.isValid()) {
            throw new CcuClientException("Unable to start CCU program " + programName);
        }
    }

    /**
     * Set a variable on the CCU.
     */
    public void setVariable(HmValueItem hmValueItem, Object value) throws CcuClientException {
        String strValue = ObjectUtils.toString(value);
        if (hmValueItem.isStringValue()) {
            strValue = "\"" + strValue + "\"";
        }
        logger.debug("Sending {} with value '{}' to CCU", hmValueItem.getName(), strValue);
        HmResult result = sendScriptByName("setVariable", HmResult.class,
                new String[] { "variable_name", "variable_state" },
                new String[] { hmValueItem.getName(), strValue });
        if (!result.isValid()) {
            throw new CcuClientException("Unable to set CCU variable " + hmValueItem.getName());
        }
    }

    /**
     * Sends a message and sets properties for the display of a 19 key Homematic
     * remote control. Used in the Homematic action.
     */
    public void setRemoteControlDisplay(String remoteControlAddress, String text, String options)
            throws CcuClientException {

        RemoteControlOptionParser rcParameterParser = new RemoteControlOptionParser();
        HmRemoteControlOptions rco = rcParameterParser.parse(remoteControlAddress, options);
        rco.setText(text);

        logger.debug("Sending to remote control {}: {}", remoteControlAddress, rco);

        HmResult result = sendScriptByName("setRemoteControlDisplay", HmResult.class,
                new String[] { "remote_address", "text", "beep_value", "backlight_value", "unit_value", "symbols" },
                new String[] { remoteControlAddress, rco.getText(), rco.getBeepAsString(),
                        rco.getBacklightAsString(), rco.getUnitAsString(),
                        StringUtils.join(rco.getSymbols(), "\t") });

        if (!result.isValid()) {
            throw new CcuClientException("Failed to set remote control with address " + remoteControlAddress);
        }
    }

    /**
     * Returns true, if the client is started.
     */
    public boolean isStarted() {
        return tclregaScripts != null;
    }

    private <T> T sendScriptByName(String scriptName, Class<T> clazz) throws CcuClientException {
        return sendScript(getTclRegaScript(scriptName), clazz);
    }

    private <T> T sendScriptByName(String scriptName, Class<T> clazz, String[] variableNames, String[] values)
            throws CcuClientException {
        String script = getTclRegaScript(scriptName);
        for (int i = 0; i < variableNames.length; i++) {
            script = StringUtils.replace(script, "{" + variableNames[i] + "}", values[i]);
        }
        return sendScript(script, clazz);
    }

    private String getTclRegaScript(String scriptName) throws CcuClientException {
        if (!isStarted()) {
            throw new CcuClientException(TclRegaScriptClient.class.getSimpleName() + " is not configured!");
        }
        return tclregaScripts.get(scriptName);
    }

    /**
     * Main method for sending a TclRega script and parsing the XML result.
     */
    @SuppressWarnings("unchecked")
    private synchronized <T> T sendScript(String script, Class<T> clazz) throws CcuClientException {
        PostMethod post = null;
        try {
            script = StringUtils.trim(script);
            if (StringUtils.isEmpty(script)) {
                throw new RuntimeException("Homematic TclRegaScript is empty!");
            }
            if (TRACE_ENABLED) {
                logger.trace("TclRegaScript: {}", script);
            }

            post = new PostMethod(context.getConfig().getTclRegaUrl());
            RequestEntity re = new ByteArrayRequestEntity(script.getBytes("ISO-8859-1"));
            post.setRequestEntity(re);
            httpClient.executeMethod(post);

            String result = post.getResponseBodyAsString();
            result = StringUtils.substringBeforeLast(result, "<xml><exec>");
            if (TRACE_ENABLED) {
                logger.trace("Result TclRegaScript: {}", result);
            }

            Unmarshaller um = JAXBContext.newInstance(clazz).createUnmarshaller();
            um.setListener(new CommonUnmarshallerListener());
            return (T) um.unmarshal(new StringReader(result));
        } catch (Exception ex) {
            throw new CcuClientException(ex.getMessage(), ex);
        } finally {
            if (post != null) {
                post.releaseConnection();
            }
        }
    }

    /**
     * Load predefined scripts from an XML file.
     */
    private Map<String, String> loadTclRegaScripts() throws CcuClientException {
        try {
            Unmarshaller um = JAXBContext.newInstance(TclScripts.class).createUnmarshaller();
            InputStream stream = Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("homematic/tclrega-scripts.xml");
            TclScripts scripts = (TclScripts) um.unmarshal(stream);

            Map<String, String> result = new HashMap<String, String>();
            for (TclScript script : scripts.getScripts()) {
                result.put(script.getName(), script.getData());
            }
            return result;
        } catch (JAXBException ex) {
            throw new CcuClientException(ex.getMessage(), ex);
        }
    }

    /**
     * Callback interface to iterate through all Homematic valueItems.
     */
    public interface TclIteratorCallback {
        /**
         * Called for every Homematic valueItem after loading from the CCU.
         */
        public void iterate(HomematicBindingConfig bindingConfig, HmValueItem hmValueItem);
    }
}