]> git.basschouten.com Git - openhab-addons.git/blob
c256eef46def1b67ad3001908ba0d080ba6305ae
[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, OnOffType.from("1".equals(on)));
150                         }
151                     } else if (command instanceof OnOffType onOffCommand) {
152                         controller
153                                 .sendCommand(String.format("%s %s", onOffCommand == 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 quantityCommand) {
165                         final QuantityType<?> converted = quantityCommand.toUnit(controller.getUnit());
166                         final String formatted = converted.format("%.1f");
167                         controller.sendCommand(String.format("temp %s %s", uid, formatted));
168                     }
169                     break;
170                 case MODE:
171                     if (command instanceof RefreshType) {
172                         final String mode = MODE_NUM_TO_STR.get(query(controller, "m"));
173                         if (mode != null) {
174                             updateState(MODE, new StringType(mode));
175                         }
176                     } else if (command instanceof StringType stringCommand) {
177                         final String mode = stringCommand.toString();
178                         controller.sendCommand(String.format("%s %s", mode, uid));
179                     }
180                     break;
181                 case FAN_SPEED:
182                     if (command instanceof RefreshType) {
183                         final String fan = FAN_NUM_TO_STR.get(query(controller, "f"));
184                         if (fan != null) {
185                             updateState(FAN_SPEED, new StringType(fan));
186                         }
187                     } else if (command instanceof StringType stringCommand) {
188                         final String fan = stringCommand.toString();
189                         controller.sendCommand(String.format("fspeed %s %s", uid, fan));
190                     }
191                     break;
192                 case LOUVRE:
193                     if (command instanceof RefreshType) {
194                         final String louvre = query(controller, "s");
195                         if (louvre != null) {
196                             updateState(LOUVRE, new StringType(louvre));
197                         }
198                     } else if (command instanceof StringType stringCommand) {
199                         final String louvre = stringCommand.toString();
200                         controller.sendCommand(String.format("swing %s %s", uid, louvre));
201                     }
202                     break;
203                 default:
204                     logger.warn("Unknown command '{}' on channel '{}' for unit '{}'", command, channel, uid);
205             }
206             updateStatus(ThingStatus.ONLINE);
207         } catch (final IOException ioe) {
208             logger.warn("Failed to handle command '{}' on channel '{}' for unit '{}' due to '{}'", command, channel,
209                     uid, ioe.getLocalizedMessage());
210             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ioe.getLocalizedMessage());
211         }
212     }
213
214     @Override
215     public void initialize() {
216         cfg = getConfigAs(HVACConfiguration.class);
217         updateStatus(ThingStatus.UNKNOWN);
218     }
219
220     /**
221      * Update this HVAC unit's properties from the controller.
222      */
223     public void refresh() {
224         for (final Channel channel : getThing().getChannels()) {
225             handleCommand(channel.getUID(), RefreshType.REFRESH);
226         }
227     }
228
229     private @Nullable String query(final ControllerHandler controller, final String queryChar)
230             throws IOException, CoolMasterClientError {
231         final String uid = getConfigAs(HVACConfiguration.class).uid;
232         final String command = String.format("query %s %s", uid, queryChar);
233         return controller.sendCommand(command);
234     }
235 }