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