]> git.basschouten.com Git - openhab-addons.git/blob
8ec6d5d4a63ebc4d1104dd3d4790568c23ffe2b5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.insteon.internal.device;
14
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19
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;
28
29 /**
30  * A DeviceFeatureListener essentially represents an openHAB item that
31  * listens to a particular feature of an Insteon device
32  *
33  * @author Daniel Pfrommer - Initial contribution
34  * @author Bernd Pfrommer - openHAB 1 insteonplm binding
35  * @author Rob Nielsen - Port to openHAB 2 insteon binding
36  */
37 @NonNullByDefault
38 public class DeviceFeatureListener {
39     private final Logger logger = LoggerFactory.getLogger(DeviceFeatureListener.class);
40
41     public enum StateChangeType {
42         ALWAYS,
43         CHANGED
44     }
45
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;
53
54     /**
55      * Constructor
56      *
57      * @param binding
58      * @param channelUID channel associated with this item
59      * @param item name of the item that is listening
60      */
61     public DeviceFeatureListener(InsteonBinding binding, ChannelUID channelUID, String item) {
62         this.binding = binding;
63         this.itemName = item;
64         this.channelUID = channelUID;
65     }
66
67     /**
68      * Gets item name
69      *
70      * @return item name
71      */
72     public String getItemName() {
73         return itemName;
74     }
75
76     /**
77      * Test if string parameter is present and has a given value
78      *
79      * @param key key to match
80      * @param value value to match
81      * @return true if key exists and value matches
82      */
83     private boolean parameterHasValue(String key, String value) {
84         String v = parameters.get(key);
85         return (v != null && v.equals(value));
86     }
87
88     /**
89      * Set parameters for this feature listener
90      *
91      * @param p the parameters to set
92      */
93     public void setParameters(Map<String, String> p) {
94         parameters = p;
95         updateRelatedDevices();
96     }
97
98     /**
99      * Publishes a state change on the openhab bus
100      *
101      * @param newState the new state to publish on the openhab bus
102      * @param changeType whether to always publish or not
103      */
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);
110         } else {
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);
115             }
116         }
117         state.put(newState.getClass(), newState);
118     }
119
120     /**
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.
124      *
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
129      */
130     public void stateChanged(State state, StateChangeType changeType, String dataKey, String dataValue) {
131         if (parameterHasValue(dataKey, dataValue)) {
132             stateChanged(state, changeType);
133         }
134     }
135
136     /**
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.
143      *
144      * @param state the new state of the feature
145      */
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;
153             }
154         }
155         pollRelatedDevices();
156         binding.updateFeatureState(channelUID, publishState);
157     }
158
159     /**
160      * Extracts related devices from the parameter list and
161      * stores them for faster access later.
162      */
163
164     private void updateRelatedDevices() {
165         String d = parameters.get("related");
166         if (d == null) {
167             return;
168         }
169         String[] devs = d.split("\\+");
170         for (String dev : devs) {
171             InsteonAddress a = InsteonAddress.parseAddress(dev);
172             relatedDevices.add(a);
173         }
174     }
175
176     /**
177      * polls all devices that are related to this item
178      * by the "related" keyword
179      */
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);
184             if (d != null) {
185                 d.doPoll(TIME_DELAY_POLL_RELATED_MSEC);
186             } else {
187                 logger.warn("device {} related to item {} is not configured!", a, itemName);
188             }
189         }
190     }
191 }