2 * Copyright (c) 2010-2024 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.openhab.binding.insteon.internal.InsteonBinding;
22 import org.openhab.core.library.types.OnOffType;
23 import org.openhab.core.library.types.PercentType;
24 import org.openhab.core.thing.ChannelUID;
25 import org.openhab.core.types.State;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * A DeviceFeatureListener essentially represents an openHAB item that
31 * listens to a particular feature of an Insteon device
33 * @author Daniel Pfrommer - Initial contribution
34 * @author Bernd Pfrommer - openHAB 1 insteonplm binding
35 * @author Rob Nielsen - Port to openHAB 2 insteon binding
38 public class DeviceFeatureListener {
39 private final Logger logger = LoggerFactory.getLogger(DeviceFeatureListener.class);
41 public enum StateChangeType {
46 private String itemName;
47 private ChannelUID channelUID;
48 private Map<String, String> parameters = new HashMap<>();
49 private Map<Class<?>, State> state = new HashMap<>();
50 private List<InsteonAddress> relatedDevices = new ArrayList<>();
51 private InsteonBinding binding;
52 private static final int TIME_DELAY_POLL_RELATED_MSEC = 5000;
58 * @param channelUID channel associated with this item
59 * @param item name of the item that is listening
61 public DeviceFeatureListener(InsteonBinding binding, ChannelUID channelUID, String item) {
62 this.binding = binding;
64 this.channelUID = channelUID;
72 public String getItemName() {
77 * Test if string parameter is present and has a given value
79 * @param key key to match
80 * @param value value to match
81 * @return true if key exists and value matches
83 private boolean parameterHasValue(String key, String value) {
84 String v = parameters.get(key);
85 return (v != null && v.equals(value));
89 * Set parameters for this feature listener
91 * @param p the parameters to set
93 public void setParameters(Map<String, String> p) {
95 updateRelatedDevices();
99 * Publishes a state change on the openhab bus
101 * @param newState the new state to publish on the openhab bus
102 * @param changeType whether to always publish or not
104 public void stateChanged(State newState, StateChangeType changeType) {
105 State oldState = state.get(newState.getClass());
106 if (oldState == null) {
107 logger.trace("new state: {}:{}", newState.getClass().getSimpleName(), newState);
108 // state has changed, must publish
109 publishState(newState);
111 logger.trace("old state: {}:{}=?{}", newState.getClass().getSimpleName(), oldState, newState);
112 // only publish if state has changed or it is requested explicitly
113 if (changeType == StateChangeType.ALWAYS || !oldState.equals(newState)) {
114 publishState(newState);
117 state.put(newState.getClass(), newState);
121 * Call this function to inform about a state change for a given
122 * parameter key and value. If dataKey and dataValue don't match,
123 * the state change will be ignored.
125 * @param state the new state to which the feature has changed
126 * @param changeType how to process the state change (always, or only when changed)
127 * @param dataKey the data key on which to filter
128 * @param dataValue the value that the data key must match for the state to be published
130 public void stateChanged(State state, StateChangeType changeType, String dataKey, String dataValue) {
131 if (parameterHasValue(dataKey, dataValue)) {
132 stateChanged(state, changeType);
137 * Publish the state. In the case of PercentType, if the value is
138 * 0, send an OnOffType.OFF and if the value is 100, send an OnOffType.ON.
139 * That way an openHAB Switch will work properly with an Insteon dimmer,
140 * as long it is used like a switch (On/Off). An openHAB DimmerItem will
141 * internally convert the ON back to 100% and OFF back to 0, so there is
142 * no need to send both 0/OFF and 100/ON.
144 * @param state the new state of the feature
146 private void publishState(State state) {
147 State publishState = state;
148 if (state instanceof PercentType) {
149 if (state.equals(PercentType.ZERO)) {
150 publishState = OnOffType.OFF;
151 } else if (state.equals(PercentType.HUNDRED)) {
152 publishState = OnOffType.ON;
155 pollRelatedDevices();
156 binding.updateFeatureState(channelUID, publishState);
160 * Extracts related devices from the parameter list and
161 * stores them for faster access later.
164 private void updateRelatedDevices() {
165 String d = parameters.get("related");
169 String[] devs = d.split("\\+");
170 for (String dev : devs) {
171 InsteonAddress a = InsteonAddress.parseAddress(dev);
172 relatedDevices.add(a);
177 * polls all devices that are related to this item
178 * by the "related" keyword
180 public void pollRelatedDevices() {
181 for (InsteonAddress a : relatedDevices) {
182 logger.debug("polling related device {} in {} ms", a, TIME_DELAY_POLL_RELATED_MSEC);
183 InsteonDevice d = binding.getDevice(a);
185 d.doPoll(TIME_DELAY_POLL_RELATED_MSEC);
187 logger.warn("device {} related to item {} is not configured!", a, itemName);