]> git.basschouten.com Git - openhab-addons.git/blob
7bbb845bd20c752397098b20bcca8dfa6ef90dc2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.coolmasternet.internal.handler;
14
15 import static org.openhab.binding.coolmasternet.internal.CoolMasterNetBindingConstants.*;
16
17 import java.io.IOException;
18 import java.util.HashMap;
19 import java.util.Map;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.coolmasternet.internal.ControllerHandler;
24 import org.openhab.binding.coolmasternet.internal.ControllerHandler.CoolMasterClientError;
25 import org.openhab.binding.coolmasternet.internal.config.HVACConfiguration;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.library.types.StringType;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.Channel;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.thing.binding.BaseThingHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link HVACHandler} is responsible for handling commands for a single
43  * HVAC unit (a single UID on a CoolMasterNet controller.)
44  *
45  * @author Angus Gratton - Initial contribution
46  * @author Wouter Born - Fix null pointer exceptions
47  */
48 @NonNullByDefault
49 public class HVACHandler extends BaseThingHandler {
50
51     /**
52      * The CoolMasterNet protocol's query command returns numbers 0-5 for fan
53      * speed, but the protocol's fan command (and matching binding command) use
54      * single-letter abbreviations.
55      */
56     private static final Map<String, String> FAN_NUM_TO_STR;
57
58     /**
59      * The CoolMasterNet query command returns numbers 0-5 for operation modes,
60      * but these don't map to any mode you can set on the device, so we use this
61      * lookup table.
62      */
63     private static final Map<String, String> MODE_NUM_TO_STR;
64
65     static {
66         FAN_NUM_TO_STR = new HashMap<>();
67         FAN_NUM_TO_STR.put("0", "l"); // low
68         FAN_NUM_TO_STR.put("1", "m"); // medium
69         FAN_NUM_TO_STR.put("2", "h"); // high
70         FAN_NUM_TO_STR.put("3", "a"); // auto
71         FAN_NUM_TO_STR.put("4", "t"); // top
72
73         MODE_NUM_TO_STR = new HashMap<>();
74         MODE_NUM_TO_STR.put("0", "cool");
75         MODE_NUM_TO_STR.put("1", "heat");
76         MODE_NUM_TO_STR.put("2", "auto");
77         MODE_NUM_TO_STR.put("3", "dry");
78         // 4=='haux' but this mode doesn't have an equivalent command to set it
79         MODE_NUM_TO_STR.put("4", "heat");
80         MODE_NUM_TO_STR.put("5", "fan");
81     }
82
83     private HVACConfiguration cfg = new HVACConfiguration();
84     private final Logger logger = LoggerFactory.getLogger(HVACHandler.class);
85
86     public HVACHandler(final Thing thing) {
87         super(thing);
88     }
89
90     /**
91      * Get the controller handler for this bridge.
92      *
93      * <p>
94      * This method does not raise any exception, but if null is returned it will
95      * always update the Thing status with the reason.
96      *
97      * <p>
98      * The returned handler may or may not be connected. This method will not
99      * change the Thing status simply because it is not connected, because a
100      * caller may wish to attempt an operation that would result in connection.
101      *
102      * @return the controller handler or null if the controller is unavailable
103      */
104     private @Nullable ControllerHandler getControllerHandler() {
105         final Bridge bridge = getBridge();
106         if (bridge == null) {
107             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
108                     "CoolMasterNet Controller bridge not configured");
109             return null;
110         }
111
112         final ControllerHandler handler = (ControllerHandler) bridge.getHandler();
113
114         if (handler == null) {
115             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
116                     "CoolMasterNet Controller bridge not initialized");
117             return null;
118         }
119
120         return handler;
121     }
122
123     @Override
124     public void handleCommand(final ChannelUID channelUID, final Command command) {
125         final ControllerHandler controller = getControllerHandler();
126         if (controller == null) {
127             return;
128         }
129
130         final String uid = cfg.uid;
131         final String channel = channelUID.getId();
132
133         try {
134             switch (channel) {
135                 case CURRENT_TEMP:
136                     if (command instanceof RefreshType) {
137                         final String currentTemp = query(controller, "a");
138                         if (currentTemp != null) {
139                             final Integer temp = Integer.parseInt(currentTemp);
140                             final QuantityType<?> value = new QuantityType<>(temp, controller.getUnit());
141                             updateState(CURRENT_TEMP, value);
142                         }
143                     }
144                     break;
145                 case ON:
146                     if (command instanceof RefreshType) {
147                         final String on = query(controller, "o");
148                         if (on != null) {
149                             updateState(ON, "1".equals(on) ? OnOffType.ON : OnOffType.OFF);
150                         }
151                     } else if (command instanceof OnOffType) {
152                         final OnOffType onoff = (OnOffType) command;
153                         controller.sendCommand(String.format("%s %s", onoff == OnOffType.ON ? "on" : "off", uid));
154                     }
155                     break;
156                 case SET_TEMP:
157                     if (command instanceof RefreshType) {
158                         final String setTemp = query(controller, "t");
159                         if (setTemp != null) {
160                             final Integer temp = Integer.parseInt(setTemp);
161                             final QuantityType<?> value = new QuantityType<>(temp, controller.getUnit());
162                             updateState(SET_TEMP, value);
163                         }
164                     } else if (command instanceof QuantityType) {
165                         final QuantityType<?> temp = (QuantityType) command;
166                         final QuantityType<?> converted = temp.toUnit(controller.getUnit());
167                         final String formatted = converted.format("%.1f");
168                         controller.sendCommand(String.format("temp %s %s", uid, formatted));
169                     }
170                     break;
171                 case MODE:
172                     if (command instanceof RefreshType) {
173                         final String mode = MODE_NUM_TO_STR.get(query(controller, "m"));
174                         if (mode != null) {
175                             updateState(MODE, new StringType(mode));
176                         }
177                     } else if (command instanceof StringType) {
178                         final String mode = ((StringType) command).toString();
179                         controller.sendCommand(String.format("%s %s", mode, uid));
180                     }
181                     break;
182                 case FAN_SPEED:
183                     if (command instanceof RefreshType) {
184                         final String fan = FAN_NUM_TO_STR.get(query(controller, "f"));
185                         if (fan != null) {
186                             updateState(FAN_SPEED, new StringType(fan));
187                         }
188                     } else if (command instanceof StringType) {
189                         final String fan = ((StringType) command).toString();
190                         controller.sendCommand(String.format("fspeed %s %s", uid, fan));
191                     }
192                     break;
193                 case LOUVRE:
194                     if (command instanceof RefreshType) {
195                         final String louvre = query(controller, "s");
196                         if (louvre != null) {
197                             updateState(LOUVRE, new StringType(louvre));
198                         }
199                     } else if (command instanceof StringType) {
200                         final String louvre = ((StringType) command).toString();
201                         controller.sendCommand(String.format("swing %s %s", uid, louvre));
202                     }
203                     break;
204                 default:
205                     logger.warn("Unknown command '{}' on channel '{}' for unit '{}'", command, channel, uid);
206             }
207             updateStatus(ThingStatus.ONLINE);
208         } catch (final IOException ioe) {
209             logger.warn("Failed to handle command '{}' on channel '{}' for unit '{}' due to '{}'", command, channel,
210                     uid, ioe.getLocalizedMessage());
211             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ioe.getLocalizedMessage());
212         }
213     }
214
215     @Override
216     public void initialize() {
217         cfg = getConfigAs(HVACConfiguration.class);
218         updateStatus(ThingStatus.UNKNOWN);
219     }
220
221     /**
222      * Update this HVAC unit's properties from the controller.
223      */
224     public void refresh() {
225         for (final Channel channel : getThing().getChannels()) {
226             handleCommand(channel.getUID(), RefreshType.REFRESH);
227         }
228     }
229
230     private @Nullable String query(final ControllerHandler controller, final String queryChar)
231             throws IOException, CoolMasterClientError {
232         final String uid = getConfigAs(HVACConfiguration.class).uid;
233         final String command = String.format("query %s %s", uid, queryChar);
234         return controller.sendCommand(command);
235     }
236 }