/** * 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.insteonplm.internal.device; import java.util.ArrayList; import java.util.HashMap; import org.openhab.binding.insteonplm.InsteonPLMActiveBinding; import org.openhab.core.events.EventPublisher; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A DeviceFeatureListener essentially represents an OpenHAB item that * listens to a particular feature of an Insteon device * * @author Daniel Pfrommer * @author Bernd Pfrommer * @since 1.6.0 */ public class DeviceFeatureListener { private static final Logger logger = LoggerFactory.getLogger(DeviceFeatureListener.class); public enum StateChangeType { ALWAYS, CHANGED }; private String m_itemName = null; private EventPublisher m_eventPublisher = null; private HashMap<String, String> m_parameters = new HashMap<String, String>(); private HashMap<Class<?>, State> m_state = new HashMap<Class<?>, State>(); private ArrayList<InsteonAddress> m_relatedDevices = new ArrayList<InsteonAddress>(); private InsteonPLMActiveBinding m_binding = null; private final static int TIME_DELAY_POLL_RELATED_MSEC = 5000; /** * Constructor * * @param item name of the item that is listening * @param eventPublisher the publisher to use for publishing on the openhab bus */ public DeviceFeatureListener(InsteonPLMActiveBinding binding, String item, EventPublisher eventPublisher) { m_binding = binding; m_itemName = item; m_eventPublisher = eventPublisher; } /** * Gets item name * * @return item name */ public String getItemName() { return m_itemName; } /** * Test if string parameter is present and has a given value * * @param key key to match * @param value value to match * @return true if key exists and value matches */ private boolean parameterHasValue(String key, String value) { if (m_parameters == null) { return false; } String v = m_parameters.get(key); return (v != null && v.equals(value)); } /** * Set parameters for this feature listener * * @param p the parameters to set */ public void setParameters(HashMap<String, String> p) { m_parameters = p; updateRelatedDevices(); } /** * Publishes a state change on the openhab bus * * @param state the new state to publish on the openhab bus * @param changeType whether to always publish or not */ public void stateChanged(State state, StateChangeType changeType) { State oldState = m_state.get(state.getClass()); if (oldState == null) { logger.trace("new state: {}:{}", state.getClass().getSimpleName(), state); // state has changed, must publish publishState(state); } else { logger.trace("old state: {}:{}=?{}", state.getClass().getSimpleName(), oldState, state); // only publish if state has changed or it is requested explicitly if (changeType == StateChangeType.ALWAYS || !oldState.equals(state)) { publishState(state); } } m_state.put(state.getClass(), state); } /** * Call this function to inform about a state change for a given * parameter key and value. If dataKey and dataValue don't match, * the state change will be ignored. * * @param state the new state to which the feature has changed * @param changeType how to process the state change (always, or only when changed) * @param dataKey the data key on which to filter * @param dataValue the value that the data key must match for the state to be published */ public void stateChanged(State state, StateChangeType changeType, String dataKey, String dataValue) { if (parameterHasValue(dataKey, dataValue)) { stateChanged(state, changeType); } } /** * Publish the state. In the case of PercentType, if the value is * 0, send a OnOffType.OFF and if the value is 100, send a OnOffType.ON. * That way an OpenHAB Switch will work properly with a Insteon dimmer, * as long it is used like a switch (On/Off). An openHAB DimmerItem will * internally convert the ON back to 100% and OFF back to 0, so there is * no need to send both 0/OFF and 100/ON. * * @param state the new state of the feature */ private void publishState(State state) { State publishState = state; if (state instanceof PercentType) { if (state.equals(PercentType.ZERO)) { publishState = OnOffType.OFF; } else if (state.equals(PercentType.HUNDRED)) { publishState = OnOffType.ON; } } pollRelatedDevices(); m_eventPublisher.postUpdate(m_itemName, publishState); } /** * Extracts related devices from the parameter list and * stores them for faster access later. */ private void updateRelatedDevices() { String d = m_parameters.get("related"); if (d == null) { return; } String[] devs = d.split("\\+"); for (String dev : devs) { InsteonAddress a = InsteonAddress.s_parseAddress(dev); if (a == null) { logger.error("invalid insteon address: {}", a); continue; } m_relatedDevices.add(a); } } /** * polls all devices that are related to this item * by the "related" keyword */ private void pollRelatedDevices() { for (InsteonAddress a : m_relatedDevices) { logger.debug("polling related device {} in {} ms", a, TIME_DELAY_POLL_RELATED_MSEC); InsteonDevice d = m_binding.getDevice(a); if (d != null) { d.doPoll(TIME_DELAY_POLL_RELATED_MSEC); } else { logger.warn("device {} related to item {} is not configured!", a, m_itemName); } } } }