org.openhab.binding.asterisk.internal.AsteriskBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.asterisk.internal.AsteriskBinding.java

Source

/**
 * Copyright (c) 2010-2015, 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.asterisk.internal;

import java.io.IOException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.asteriskjava.manager.AuthenticationFailedException;
import org.asteriskjava.manager.ManagerConnection;
import org.asteriskjava.manager.ManagerConnectionFactory;
import org.asteriskjava.manager.ManagerEventListener;
import org.asteriskjava.manager.action.StatusAction;
import org.asteriskjava.manager.event.HangupEvent;
import org.asteriskjava.manager.event.ManagerEvent;
import org.asteriskjava.manager.event.NewChannelEvent;
import org.openhab.binding.asterisk.AsteriskBindingProvider;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.library.tel.items.CallItem;
import org.openhab.library.tel.types.CallType;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Asterisk binding connects to a Manager Interface of an Asterisk VOIP PBX
 * and listens to event notifications from this box. 
 * 
 * @author Thomas.Eichstaedt-Engelen
 * @since 0.9.0
 */
public class AsteriskBinding extends AbstractBinding<AsteriskBindingProvider> implements ManagedService {

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

    protected static ManagerConnection managerConnection;

    /** The hostname of the Asterisk Manager Interface to connect to */
    protected static String host;
    /** The username to connect to the Manager Interface */
    protected static String username;
    /** The password to connect to the Manager Interface */
    protected static String password;

    public void activate() {
    }

    public void deactivate() {
        disconnect();
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("rawtypes")
    public void updated(Dictionary config) throws ConfigurationException {
        if (config != null) {
            disconnect();

            AsteriskBinding.host = (String) config.get("host");
            AsteriskBinding.username = (String) config.get("username");
            AsteriskBinding.password = (String) config.get("password");

            if (StringUtils.isNotBlank(AsteriskBinding.host) && StringUtils.isNotBlank(AsteriskBinding.username)) {
                connect(AsteriskBinding.host, AsteriskBinding.username, AsteriskBinding.password);
            } else {
                logger.warn(
                        "cannot connect to asterisk manager interface because of missing "
                                + "parameters (host={}, username={})",
                        AsteriskBinding.host, AsteriskBinding.username);
            }
        }
    }

    /**
     * Connects to <code>host</code> and opens a ManagerConnection by using the
     * given <code>username</code> and <code>password</code>. Note: The Asterisk
     * ManagerInterface on your Asterisk PBX is deactivated by default. Please
     * refer to the documentation how to activate the ManagerInterface (AMI).
     * 
     * @param host the where to find the Asterisk PBX
     * @param username username to login to Asterisk ManagerInterface
     * @param password password to login to Asterisk ManagerInterface
     */
    private void connect(String host, String username, String password) {
        ManagerConnectionFactory factory = new ManagerConnectionFactory(host, username, password);

        AsteriskBinding.managerConnection = factory.createManagerConnection();
        AsteriskBinding.managerConnection.addEventListener(new AsteriskEventManager());

        try {
            AsteriskBinding.managerConnection.login();
        } catch (AuthenticationFailedException afe) {
            logger.error("authentication failed, please verify username and password");
        } catch (IOException ioe) {
            logger.error("Could not connect to ManagerInterface on {}: {}", host, ioe.toString());
        } catch (Exception e) {
            logger.error("Login to Asterisk Manager-Interface on {} throws exception: {}", host, e.toString());
        }

        try {
            AsteriskBinding.managerConnection.sendAction(new StatusAction());
        } catch (Exception e) {
            logger.error("registering for status update throws exception: {}", e.toString());
        }
    }

    /**
     * Disconnects from the current Asterisk ManagerInterface.
     */
    private void disconnect() {
        if (AsteriskBinding.managerConnection != null) {
            AsteriskBinding.managerConnection.logoff();
            AsteriskBinding.managerConnection = null;
        }
    }

    /**
     * @author Thomas.Eichstaedt-Engelen
     */
    private class AsteriskEventManager implements ManagerEventListener {

        /** holds call details of the currently active calls */
        protected Map<String, CallType> eventCache;

        public AsteriskEventManager() {
            eventCache = new HashMap<String, CallType>();
        }

        /**
         * @{inheritDoc}
         */
        public void onManagerEvent(ManagerEvent managerEvent) {
            for (AsteriskBindingProvider provider : providers) {
                for (AsteriskBindingTypes type : AsteriskBindingTypes.values()) {
                    for (String itemName : provider.getItemNamesByType(type)) {
                        Class<? extends Item> itemType = provider.getItemType(itemName);
                        handleManagerEvent(itemName, itemType, managerEvent);
                    }
                }
            }
        }

        /**
         * Dispatches the given <code>managerEvent</code> to the specialized
         * handler methods.
         * 
         * @param itemName the corresponding item
         * @param itemType the Type of the corresponding item
         * @param managerEvent the {@link ManagerEvent} to dispatch
         */
        private void handleManagerEvent(String itemName, Class<? extends Item> itemType,
                ManagerEvent managerEvent) {
            if (managerEvent instanceof NewChannelEvent) {
                handleNewCall(itemName, itemType, (NewChannelEvent) managerEvent);
            } else if (managerEvent instanceof HangupEvent) {
                handleHangupCall(itemName, itemType, (HangupEvent) managerEvent);
            }
        }

        private void handleNewCall(String itemName, Class<? extends Item> itemType, NewChannelEvent event) {
            if (event.getCallerIdNum() == null || event.getExten() == null) {
                logger.debug("calleridnum or exten is null -> handle new call aborted!");
                return;
            }

            CallType call = new CallType(new StringType(event.getCallerIdNum()), new StringType(event.getExten()));
            eventCache.put(event.getUniqueId(), call);

            if (itemType.isAssignableFrom(SwitchItem.class)) {
                eventPublisher.postUpdate(itemName, OnOffType.ON);
            } else if (itemType.isAssignableFrom(CallItem.class)) {
                eventPublisher.postUpdate(itemName, call);
            } else {
                logger.warn("handle call for item type '{}' is undefined", itemName);
            }
        }

        /**
         * Removes <code>event</code> from the <code>eventCache</code> and posts
         * updates according to the content of the <code>eventCache</code>. If
         * there is no active call left we send an OFF-State (resp. empty 
         * {@link CallType} and ON-State (one of the remaining active calls)
         * in all other cases. 
         * 
         * @param itemName
         * @param itemType 
         * @param event
         */
        private void handleHangupCall(String itemName, Class<? extends Item> itemType, HangupEvent event) {
            eventCache.remove(event.getUniqueId());
            if (itemType.isAssignableFrom(SwitchItem.class)) {
                OnOffType activeState = (eventCache.size() == 0 ? OnOffType.OFF : OnOffType.ON);
                eventPublisher.postUpdate(itemName, activeState);
            } else if (itemType.isAssignableFrom(CallItem.class)) {
                CallType call = (CallType) (eventCache.size() == 0 ? CallType.EMPTY
                        : eventCache.values().toArray()[0]);
                eventPublisher.postUpdate(itemName, call);
            } else {
                logger.warn("handleHangupCall - postUpdate for itemType '{}' is undefined", itemName);
            }
        }

    }

}