2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.insteon.internal.device;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.insteon.internal.InsteonBinding;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.types.State;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * A DeviceFeatureListener essentially represents an openHAB item that
32 * listens to a particular feature of an Insteon device
34 * @author Daniel Pfrommer - Initial contribution
35 * @author Bernd Pfrommer - openHAB 1 insteonplm binding
36 * @author Rob Nielsen - Port to openHAB 2 insteon binding
39 public class DeviceFeatureListener {
40 private final Logger logger = LoggerFactory.getLogger(DeviceFeatureListener.class);
42 public enum StateChangeType {
47 private String itemName;
48 private ChannelUID channelUID;
49 private Map<String, @Nullable String> parameters = new HashMap<>();
50 private Map<Class<?>, @Nullable State> state = new HashMap<>();
51 private List<InsteonAddress> relatedDevices = new ArrayList<>();
52 private InsteonBinding binding;
53 private static final int TIME_DELAY_POLL_RELATED_MSEC = 5000;
58 * @param item name of the item that is listening
59 * @param channelUID channel associated with this item
60 * @param eventPublisher the publisher to use for publishing on the openhab bus
62 public DeviceFeatureListener(InsteonBinding binding, ChannelUID channelUID, String item) {
63 this.binding = binding;
65 this.channelUID = channelUID;
73 public String getItemName() {
78 * Test if string parameter is present and has a given value
80 * @param key key to match
81 * @param value value to match
82 * @return true if key exists and value matches
84 private boolean parameterHasValue(String key, String value) {
85 String v = parameters.get(key);
86 return (v != null && v.equals(value));
90 * Set parameters for this feature listener
92 * @param p the parameters to set
94 public void setParameters(Map<String, @Nullable String> p) {
96 updateRelatedDevices();
100 * Publishes a state change on the openhab bus
102 * @param newState the new state to publish on the openhab bus
103 * @param changeType whether to always publish or not
105 public void stateChanged(State newState, StateChangeType changeType) {
106 State oldState = state.get(newState.getClass());
107 if (oldState == null) {
108 logger.trace("new state: {}:{}", newState.getClass().getSimpleName(), newState);
109 // state has changed, must publish
110 publishState(newState);
112 logger.trace("old state: {}:{}=?{}", newState.getClass().getSimpleName(), oldState, newState);
113 // only publish if state has changed or it is requested explicitly
114 if (changeType == StateChangeType.ALWAYS || !oldState.equals(newState)) {
115 publishState(newState);
118 state.put(newState.getClass(), newState);
122 * Call this function to inform about a state change for a given
123 * parameter key and value. If dataKey and dataValue don't match,
124 * the state change will be ignored.
126 * @param state the new state to which the feature has changed
127 * @param changeType how to process the state change (always, or only when changed)
128 * @param dataKey the data key on which to filter
129 * @param dataValue the value that the data key must match for the state to be published
131 public void stateChanged(State state, StateChangeType changeType, String dataKey, String dataValue) {
132 if (parameterHasValue(dataKey, dataValue)) {
133 stateChanged(state, changeType);
138 * Publish the state. In the case of PercentType, if the value is
139 * 0, send a OnOffType.OFF and if the value is 100, send a OnOffType.ON.
140 * That way an openHAB Switch will work properly with a Insteon dimmer,
141 * as long it is used like a switch (On/Off). An openHAB DimmerItem will
142 * internally convert the ON back to 100% and OFF back to 0, so there is
143 * no need to send both 0/OFF and 100/ON.
145 * @param state the new state of the feature
147 private void publishState(State state) {
148 State publishState = state;
149 if (state instanceof PercentType) {
150 if (state.equals(PercentType.ZERO)) {
151 publishState = OnOffType.OFF;
152 } else if (state.equals(PercentType.HUNDRED)) {
153 publishState = OnOffType.ON;
156 pollRelatedDevices();
157 binding.updateFeatureState(channelUID, publishState);
161 * Extracts related devices from the parameter list and
162 * stores them for faster access later.
165 private void updateRelatedDevices() {
166 String d = parameters.get("related");
170 String[] devs = d.split("\\+");
171 for (String dev : devs) {
172 InsteonAddress a = InsteonAddress.parseAddress(dev);
173 relatedDevices.add(a);
178 * polls all devices that are related to this item
179 * by the "related" keyword
181 public void pollRelatedDevices() {
182 for (InsteonAddress a : relatedDevices) {
183 logger.debug("polling related device {} in {} ms", a, TIME_DELAY_POLL_RELATED_MSEC);
184 InsteonDevice d = binding.getDevice(a);
186 d.doPoll(TIME_DELAY_POLL_RELATED_MSEC);
188 logger.warn("device {} related to item {} is not configured!", a, itemName);