]> git.basschouten.com Git - openhab-addons.git/blob
1308bf201331ffcabc8647c828cbd5eb64c68ecb
[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.digitalstrom.internal.handler;
14
15 import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.BINDING_ID;
16
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23
24 import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
25 import org.openhab.binding.digitalstrom.internal.lib.climate.TemperatureControlSensorTransmitter;
26 import org.openhab.binding.digitalstrom.internal.lib.climate.constants.ControlModes;
27 import org.openhab.binding.digitalstrom.internal.lib.climate.constants.ControlStates;
28 import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
29 import org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener;
30 import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
31 import org.openhab.binding.digitalstrom.internal.lib.manager.impl.TemperatureControlManager;
32 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FunctionalColorGroupEnum;
33 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
34 import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
35 import org.openhab.core.config.core.Configuration;
36 import org.openhab.core.library.types.DecimalType;
37 import org.openhab.core.library.types.IncreaseDecreaseType;
38 import org.openhab.core.library.types.OnOffType;
39 import org.openhab.core.library.types.PercentType;
40 import org.openhab.core.thing.Bridge;
41 import org.openhab.core.thing.Channel;
42 import org.openhab.core.thing.ChannelUID;
43 import org.openhab.core.thing.Thing;
44 import org.openhab.core.thing.ThingStatus;
45 import org.openhab.core.thing.ThingStatusDetail;
46 import org.openhab.core.thing.ThingStatusInfo;
47 import org.openhab.core.thing.ThingTypeUID;
48 import org.openhab.core.thing.binding.BaseThingHandler;
49 import org.openhab.core.thing.binding.ThingHandler;
50 import org.openhab.core.thing.binding.builder.ChannelBuilder;
51 import org.openhab.core.thing.binding.builder.ThingBuilder;
52 import org.openhab.core.thing.type.ChannelTypeUID;
53 import org.openhab.core.types.Command;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * The {@link ZoneTemperatureControlHandler} is responsible for handling the configuration, to load the supported
59  * channel of a
60  * digitalSTROM zone, which has a temperature control configured, and handling commands, which are sent to the channel.
61  * <br>
62  * <br>
63  * For that it uses the {@link BridgeHandler} to register itself as {@link TemperatureControlStatusListener} at the
64  * {@link TemperatureControlManager} to get informed by status changes. Through the registration as
65  * {@link TemperatureControlStatusListener} a {@link TemperatureControlSensorTransmitter} will be registered to this
66  * {@link ZoneTemperatureControlHandler}, which is needed to set the temperature or the control value of a zone.
67  *
68  * @author Michael Ochel - Initial contribution
69  * @author Matthias Siegele - Initial contribution
70  */
71 public class ZoneTemperatureControlHandler extends BaseThingHandler implements TemperatureControlStatusListener {
72
73     /**
74      * Contains all supported thing types of this handler
75      */
76     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(
77             Arrays.asList(DigitalSTROMBindingConstants.THING_TYPE_ZONE_TEMERATURE_CONTROL));
78
79     private final Logger logger = LoggerFactory.getLogger(ZoneTemperatureControlHandler.class);
80
81     private TemperatureControlSensorTransmitter temperatureSensorTransmitter;
82
83     private BridgeHandler dssBridgeHandler;
84     private Integer zoneID;
85     private String currentChannelID;
86     private Float currentValue = 0f;
87     private final Float step = 1f;
88
89     // check zoneID error codes
90     public static final int ZONE_ID_NOT_EXISTS = -1;
91     public static final int ZONE_ID_NOT_SET = -2;
92     public static final int BRIDGE_IS_NULL = -3;
93
94     /**
95      * Creates a new {@link ZoneTemperatureControlHandler}.
96      *
97      * @param thing must not be null
98      */
99     public ZoneTemperatureControlHandler(Thing thing) {
100         super(thing);
101     }
102
103     @Override
104     public void initialize() {
105         logger.debug("Initializing DeviceHandler.");
106         if (getConfig().get(DigitalSTROMBindingConstants.ZONE_ID) != null) {
107             final Bridge bridge = getBridge();
108             if (bridge != null) {
109                 bridgeStatusChanged(bridge.getStatusInfo());
110             } else {
111                 // Set status to OFFLINE, if no bridge is available e.g. because the bridge has been removed and the
112                 // Thing was reinitialized.
113                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
114             }
115         } else {
116             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "zoneID is missing");
117         }
118     }
119
120     /**
121      * Returns the configured zoneID of the given {@link Configuration}. If the zoneID does't exist or can't be checked
122      * {@link #ZONE_ID_NOT_EXISTS}, {@link #ZONE_ID_NOT_SET} or {@link #BRIDGE_IS_NULL} will be returned.
123      *
124      * @param config the {@link Configuration} to be checked
125      * @param bridge the responsible {@link BridgeHandler}
126      * @return zoneID the existing dS zoneID or a error constant
127      */
128     public static int getZoneID(Configuration config, BridgeHandler bridge) {
129         if (config == null || config.get(DigitalSTROMBindingConstants.ZONE_ID) == null) {
130             return ZONE_ID_NOT_SET;
131         }
132         if (bridge == null) {
133             return BRIDGE_IS_NULL;
134         }
135         String configZoneID = config.get(DigitalSTROMBindingConstants.ZONE_ID).toString();
136         int zoneID;
137         StructureManager strucMan = bridge.getStructureManager();
138         if (strucMan != null) {
139             try {
140                 zoneID = Integer.parseInt(configZoneID);
141                 if (!strucMan.checkZoneID(zoneID)) {
142                     zoneID = ZONE_ID_NOT_EXISTS;
143                 }
144             } catch (NumberFormatException e) {
145                 zoneID = strucMan.getZoneId(configZoneID);
146             }
147             return zoneID;
148         }
149         return ZONE_ID_NOT_EXISTS;
150     }
151
152     @Override
153     public void dispose() {
154         logger.debug("Handler disposed... unregister DeviceStatusListener");
155         if (zoneID != null) {
156             if (dssBridgeHandler != null) {
157                 dssBridgeHandler.unregisterTemperatureControlStatusListener(this);
158             }
159             temperatureSensorTransmitter = null;
160         }
161     }
162
163     @Override
164     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
165         if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
166             int tempZoneID = getZoneID(getConfig(), getDssBridgeHandler());
167             if (tempZoneID == ZONE_ID_NOT_EXISTS) {
168                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
169                         "Configured zone '" + getConfig().get(DigitalSTROMBindingConstants.ZONE_ID)
170                                 + "' does not exist, please check the configuration.");
171             } else {
172                 this.zoneID = tempZoneID;
173             }
174             if (zoneID != null) {
175                 if (getDssBridgeHandler() != null && temperatureSensorTransmitter == null) {
176                     dssBridgeHandler.registerTemperatureControlStatusListener(this);
177                     updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
178                             "waiting for listener registration");
179                 } else {
180                     updateStatus(ThingStatus.ONLINE);
181                 }
182             } else {
183                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No zoneID is set!");
184             }
185         }
186         if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
187             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
188         }
189         if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
190             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
191         }
192         logger.debug("Set status to {}", getThing().getStatusInfo());
193     }
194
195     @Override
196     public void handleCommand(ChannelUID channelUID, Command command) {
197         BridgeHandler dssBridgeHandler = getDssBridgeHandler();
198         if (dssBridgeHandler == null) {
199             logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
200             return;
201         }
202         if (temperatureSensorTransmitter == null && zoneID != null) {
203             logger.debug(
204                     "Device not known on TemperationControlManager or temperatureSensorTransreciver is not registerd. Cannot handle command.");
205             return;
206         }
207         if (channelUID.getId().equals(currentChannelID)) {
208             if (command instanceof PercentType || command instanceof DecimalType) {
209                 sendCommandAndUpdateChannel(((DecimalType) command).floatValue());
210             } else if (command instanceof OnOffType) {
211                 if (OnOffType.ON.equals(command)) {
212                     if (isTemperature()) {
213                         sendCommandAndUpdateChannel(TemperatureControlSensorTransmitter.MAX_TEMP);
214                     } else {
215                         sendCommandAndUpdateChannel(TemperatureControlSensorTransmitter.MAX_CONTROLL_VALUE);
216                     }
217                 } else {
218                     if (isTemperature()) {
219                         sendCommandAndUpdateChannel(0f);
220                     } else {
221                         sendCommandAndUpdateChannel(TemperatureControlSensorTransmitter.MIN_CONTROLL_VALUE);
222                     }
223                 }
224             } else if (command instanceof IncreaseDecreaseType) {
225                 if (IncreaseDecreaseType.INCREASE.equals(command)) {
226                     sendCommandAndUpdateChannel(currentValue + step);
227                 } else {
228                     sendCommandAndUpdateChannel(currentValue - step);
229                 }
230             }
231         } else {
232             logger.debug("Command sent to an unknown channel id: {}", channelUID);
233         }
234     }
235
236     private boolean isTemperature() {
237         return currentChannelID.contains(DsChannelTypeProvider.TEMPERATURE_CONTROLLED);
238     }
239
240     private void sendCommandAndUpdateChannel(Float newValue) {
241         if (isTemperature()) {
242             if (temperatureSensorTransmitter.pushTargetTemperature(zoneID, newValue)) {
243                 currentValue = newValue;
244                 updateState(currentChannelID, new DecimalType(newValue));
245             }
246         } else {
247             if (temperatureSensorTransmitter.pushControlValue(zoneID, newValue)) {
248                 currentValue = newValue;
249                 updateState(currentChannelID, new PercentType(newValue.intValue()));
250             }
251         }
252     }
253
254     private synchronized BridgeHandler getDssBridgeHandler() {
255         if (this.dssBridgeHandler == null) {
256             Bridge bridge = getBridge();
257             if (bridge == null) {
258                 logger.debug("Bride cannot be found");
259                 return null;
260             }
261             ThingHandler handler = bridge.getHandler();
262
263             if (handler instanceof BridgeHandler) {
264                 dssBridgeHandler = (BridgeHandler) handler;
265             } else {
266                 return null;
267             }
268         }
269         return dssBridgeHandler;
270     }
271
272     @Override
273     public synchronized void configChanged(TemperatureControlStatus tempControlStatus) {
274         if (tempControlStatus != null && tempControlStatus.isNotSetOff()) {
275             ControlModes controlMode = ControlModes.getControlMode(tempControlStatus.getControlMode());
276             ControlStates controlState = ControlStates.getControlState(tempControlStatus.getControlState());
277             if (controlMode != null && controlState != null) {
278                 logger.debug("config changed: {}", tempControlStatus.toString());
279                 if (controlMode.equals(ControlModes.OFF) && currentChannelID != null) {
280                     currentChannelID = null;
281                     loadChannel();
282                 } else if (controlMode.equals(ControlModes.PID_CONTROL)
283                         && (currentChannelID == null
284                                 || !currentChannelID.contains(DsChannelTypeProvider.TEMPERATURE_CONTROLLED))
285                         && !controlState.equals(ControlStates.EMERGENCY)) {
286                     currentChannelID = DsChannelTypeProvider.getOutputChannelTypeID(FunctionalColorGroupEnum.BLUE,
287                             OutputModeEnum.TEMPRETURE_PWM);
288                     loadChannel();
289                     currentValue = tempControlStatus.getNominalValue();
290                     updateState(currentChannelID, new DecimalType(currentValue.doubleValue()));
291                 } else if (!controlMode.equals(ControlModes.PID_CONTROL) && !controlMode.equals(ControlModes.OFF)) {
292                     currentChannelID = DsChannelTypeProvider.getOutputChannelTypeID(FunctionalColorGroupEnum.BLUE,
293                             OutputModeEnum.HEATING_PWM);
294                     loadChannel();
295                     currentValue = tempControlStatus.getControlValue();
296                     updateState(currentChannelID, new PercentType(fixPercent(currentValue.intValue())));
297                     if (controlState.equals(ControlStates.EMERGENCY)) {
298                         updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR,
299                                 "The communication with temperation sensor fails. Temperature control state emergency (temperature control though the control value) is active.");
300                     }
301                 }
302                 Map<String, String> properties = editProperties();
303                 properties.put("controlDSUID", tempControlStatus.getControlDSUID());
304                 properties.put("controlMode", controlMode.getKey());
305                 properties.put("controlState", controlState.getKey());
306                 updateProperties(properties);
307             }
308         } else {
309             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
310                     "digitalSTROM temperature control is for this zone not configured in.");
311         }
312     }
313
314     private synchronized void loadChannel() {
315         List<Channel> newChannelList = new ArrayList<>(1);
316         if (currentChannelID != null) {
317             newChannelList.add(ChannelBuilder
318                     .create(new ChannelUID(this.getThing().getUID(), currentChannelID),
319                             DsChannelTypeProvider.getItemType(currentChannelID))
320                     .withType(new ChannelTypeUID(BINDING_ID, currentChannelID)).build());
321         }
322         ThingBuilder thingBuilder = editThing();
323         thingBuilder.withChannels(newChannelList);
324         updateThing(thingBuilder.build());
325         logger.debug("load channel: {} with item: {}", currentChannelID,
326                 DsChannelTypeProvider.getItemType(currentChannelID));
327     }
328
329     @Override
330     public synchronized void onTargetTemperatureChanged(Float newValue) {
331         if (isTemperature()) {
332             updateState(currentChannelID, new DecimalType(newValue));
333         }
334     }
335
336     @Override
337     public synchronized void onControlValueChanged(Integer newValue) {
338         if (!isTemperature()) {
339             updateState(currentChannelID, new PercentType(fixPercent(newValue)));
340         }
341     }
342
343     private int fixPercent(int value) {
344         return value < 0 ? 0 : value > 100 ? 100 : value;
345     }
346
347     @Override
348     public void registerTemperatureSensorTransmitter(
349             TemperatureControlSensorTransmitter temperatureSensorTransreciver) {
350         updateStatus(ThingStatus.ONLINE);
351         this.temperatureSensorTransmitter = temperatureSensorTransreciver;
352     }
353
354     @Override
355     public Integer getTemperationControlStatusListenrID() {
356         return zoneID;
357     }
358 }