]> git.basschouten.com Git - openhab-addons.git/blob
21bab3dda4a278401c1e19506d6ff445de56e042
[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.daikin.internal.handler;
14
15 import java.math.BigDecimal;
16 import java.util.Objects;
17 import java.util.Optional;
18 import java.util.stream.IntStream;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.openhab.binding.daikin.internal.DaikinBindingConstants;
24 import org.openhab.binding.daikin.internal.DaikinCommunicationException;
25 import org.openhab.binding.daikin.internal.DaikinDynamicStateDescriptionProvider;
26 import org.openhab.binding.daikin.internal.api.ControlInfo;
27 import org.openhab.binding.daikin.internal.api.EnergyInfoDayAndWeek;
28 import org.openhab.binding.daikin.internal.api.EnergyInfoYear;
29 import org.openhab.binding.daikin.internal.api.Enums.FanMovement;
30 import org.openhab.binding.daikin.internal.api.Enums.FanSpeed;
31 import org.openhab.binding.daikin.internal.api.Enums.HomekitMode;
32 import org.openhab.binding.daikin.internal.api.Enums.Mode;
33 import org.openhab.binding.daikin.internal.api.Enums.SpecialMode;
34 import org.openhab.binding.daikin.internal.api.SensorInfo;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.OnOffType;
37 import org.openhab.core.library.types.QuantityType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.library.unit.Units;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.types.Command;
43 import org.openhab.core.types.State;
44 import org.openhab.core.types.UnDefType;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * Handles communicating with a Daikin air conditioning unit.
50  *
51  * @author Tim Waterhouse - Initial Contribution
52  * @author Paul Smedley - Modifications to support Airbase Controllers
53  * @author Lukas Agethen - Added support for Energy Year reading, compressor frequency and powerful mode
54  * @author Wouter Denayer - Added to support for weekly and daily energy reading
55  *
56  */
57 @NonNullByDefault
58 public class DaikinAcUnitHandler extends DaikinBaseHandler {
59     private final Logger logger = LoggerFactory.getLogger(DaikinAcUnitHandler.class);
60
61     private Optional<Integer> autoModeValue = Optional.empty();
62
63     public DaikinAcUnitHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider,
64             @Nullable HttpClient httpClient) {
65         super(thing, stateDescriptionProvider, httpClient);
66     }
67
68     @Override
69     protected void pollStatus() throws DaikinCommunicationException {
70         ControlInfo controlInfo = webTargets.getControlInfo();
71         if (!"OK".equals(controlInfo.ret)) {
72             throw new DaikinCommunicationException("Invalid response from host");
73         }
74
75         updateState(DaikinBindingConstants.CHANNEL_AC_POWER, OnOffType.from(controlInfo.power));
76         updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
77
78         updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name()));
79         updateState(DaikinBindingConstants.CHANNEL_AC_FAN_SPEED, new StringType(controlInfo.fanSpeed.name()));
80         updateState(DaikinBindingConstants.CHANNEL_AC_FAN_DIR, new StringType(controlInfo.fanMovement.name()));
81
82         if (!controlInfo.power) {
83             updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue()));
84         } else if (controlInfo.mode == Mode.COLD) {
85             updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue()));
86         } else if (controlInfo.mode == Mode.HEAT) {
87             updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue()));
88         } else if (controlInfo.mode == Mode.AUTO) {
89             updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue()));
90         }
91
92         if (controlInfo.advancedMode.isUndefined()) {
93             updateState(DaikinBindingConstants.CHANNEL_AC_STREAMER, UnDefType.UNDEF);
94             updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE, UnDefType.UNDEF);
95         } else {
96             updateState(DaikinBindingConstants.CHANNEL_AC_STREAMER,
97                     OnOffType.from(controlInfo.advancedMode.isStreamerActive()));
98             updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE,
99                     new StringType(controlInfo.getSpecialMode().name()));
100         }
101
102         SensorInfo sensorInfo = webTargets.getSensorInfo();
103         updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp);
104
105         updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp);
106
107         if (sensorInfo.indoorhumidity.isPresent()) {
108             updateState(DaikinBindingConstants.CHANNEL_HUMIDITY,
109                     new QuantityType<>(sensorInfo.indoorhumidity.get(), Units.PERCENT));
110         } else {
111             updateState(DaikinBindingConstants.CHANNEL_HUMIDITY, UnDefType.UNDEF);
112         }
113
114         if (sensorInfo.compressorfrequency.isPresent()) {
115             updateState(DaikinBindingConstants.CHANNEL_CMP_FREQ,
116                     new QuantityType<>(sensorInfo.compressorfrequency.get(), Units.PERCENT));
117         } else {
118             updateState(DaikinBindingConstants.CHANNEL_CMP_FREQ, UnDefType.UNDEF);
119         }
120
121         try {
122             EnergyInfoYear energyInfoYear = webTargets.getEnergyInfoYear();
123
124             if (energyInfoYear.energyHeatingThisYear.isPresent()) {
125                 updateEnergyYearChannel(DaikinBindingConstants.CHANNEL_ENERGY_HEATING_CURRENTYEAR,
126                         energyInfoYear.energyHeatingThisYear);
127             }
128             if (energyInfoYear.energyCoolingThisYear.isPresent()) {
129                 updateEnergyYearChannel(DaikinBindingConstants.CHANNEL_ENERGY_COOLING_CURRENTYEAR,
130                         energyInfoYear.energyCoolingThisYear);
131             }
132         } catch (DaikinCommunicationException e) {
133             // Suppress any error if energy info is not supported.
134             logger.debug("getEnergyInfoYear() error: {}", e.getMessage());
135         }
136
137         try {
138             EnergyInfoDayAndWeek energyInfoDayAndWeek = webTargets.getEnergyInfoDayAndWeek();
139
140             updateEnergyDayAndWeekChannel(DaikinBindingConstants.CHANNEL_ENERGY_HEATING_TODAY,
141                     energyInfoDayAndWeek.energyHeatingToday);
142             updateEnergyDayAndWeekChannel(DaikinBindingConstants.CHANNEL_ENERGY_HEATING_THISWEEK,
143                     energyInfoDayAndWeek.energyHeatingThisWeek);
144             updateEnergyDayAndWeekChannel(DaikinBindingConstants.CHANNEL_ENERGY_HEATING_LASTWEEK,
145                     energyInfoDayAndWeek.energyHeatingLastWeek);
146             updateEnergyDayAndWeekChannel(DaikinBindingConstants.CHANNEL_ENERGY_COOLING_TODAY,
147                     energyInfoDayAndWeek.energyCoolingToday);
148             updateEnergyDayAndWeekChannel(DaikinBindingConstants.CHANNEL_ENERGY_COOLING_THISWEEK,
149                     energyInfoDayAndWeek.energyCoolingThisWeek);
150             updateEnergyDayAndWeekChannel(DaikinBindingConstants.CHANNEL_ENERGY_COOLING_LASTWEEK,
151                     energyInfoDayAndWeek.energyCoolingLastWeek);
152         } catch (DaikinCommunicationException e) {
153             // Suppress any error if energy info is not supported.
154             logger.debug("getEnergyInfoDayAndWeek() error: {}", e.getMessage());
155         }
156     }
157
158     @Override
159     protected boolean handleCommandInternal(ChannelUID channelUID, Command command)
160             throws DaikinCommunicationException {
161         switch (channelUID.getId()) {
162             case DaikinBindingConstants.CHANNEL_AC_FAN_DIR:
163                 if (command instanceof StringType stringCommand) {
164                     changeFanDir(stringCommand.toString());
165                     return true;
166                 }
167                 break;
168             case DaikinBindingConstants.CHANNEL_AC_SPECIALMODE:
169                 if (command instanceof StringType stringCommand) {
170                     changeSpecialMode(stringCommand.toString());
171                     return true;
172                 }
173                 break;
174             case DaikinBindingConstants.CHANNEL_AC_STREAMER:
175                 if (command instanceof OnOffType onOffCommand) {
176                     changeStreamer(onOffCommand.equals(OnOffType.ON));
177                     return true;
178                 }
179                 break;
180         }
181         return false;
182     }
183
184     @Override
185     protected void changePower(boolean power) throws DaikinCommunicationException {
186         ControlInfo info = webTargets.getControlInfo();
187         info.power = power;
188         webTargets.setControlInfo(info);
189     }
190
191     @Override
192     protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException {
193         ControlInfo info = webTargets.getControlInfo();
194         info.temp = Optional.of(newTemperature);
195         webTargets.setControlInfo(info);
196     }
197
198     @Override
199     protected void changeMode(String mode) throws DaikinCommunicationException {
200         Mode newMode;
201         try {
202             newMode = Mode.valueOf(mode);
203         } catch (IllegalArgumentException ex) {
204             logger.warn("Invalid mode: {}. Valid values: {}", mode, Mode.values());
205             return;
206         }
207         ControlInfo info = webTargets.getControlInfo();
208         info.mode = newMode;
209         if (autoModeValue.isPresent()) {
210             info.autoModeValue = autoModeValue.get();
211         }
212         boolean accepted = webTargets.setControlInfo(info);
213
214         // If mode=0 is not accepted try AUTO1 (mode=1)
215         if (!accepted && newMode == Mode.AUTO && autoModeValue.isEmpty()) {
216             info.autoModeValue = Mode.AUTO1.getValue();
217             if (webTargets.setControlInfo(info)) {
218                 autoModeValue = Optional.of(info.autoModeValue);
219                 logger.debug("AUTO uses mode={}", info.autoModeValue);
220             } else {
221                 logger.warn("AUTO mode not accepted with mode=0 or mode=1");
222             }
223         }
224     }
225
226     @Override
227     protected void changeFanSpeed(String fanSpeed) throws DaikinCommunicationException {
228         FanSpeed newSpeed;
229         try {
230             newSpeed = FanSpeed.valueOf(fanSpeed);
231         } catch (IllegalArgumentException ex) {
232             logger.warn("Invalid fan speed: {}. Valid values: {}", fanSpeed, FanSpeed.values());
233             return;
234         }
235         ControlInfo info = webTargets.getControlInfo();
236         info.fanSpeed = newSpeed;
237         webTargets.setControlInfo(info);
238     }
239
240     protected void changeFanDir(String fanDir) throws DaikinCommunicationException {
241         FanMovement newMovement;
242         try {
243             newMovement = FanMovement.valueOf(fanDir);
244         } catch (IllegalArgumentException ex) {
245             logger.warn("Invalid fan direction: {}. Valid values: {}", fanDir, FanMovement.values());
246             return;
247         }
248         ControlInfo info = webTargets.getControlInfo();
249         info.fanMovement = newMovement;
250         webTargets.setControlInfo(info);
251     }
252
253     protected void changeSpecialMode(String specialMode) throws DaikinCommunicationException {
254         SpecialMode newMode;
255         try {
256             newMode = SpecialMode.valueOf(specialMode);
257         } catch (IllegalArgumentException e) {
258             logger.warn("Invalid specialmode: {}. Valid values: {}", specialMode, SpecialMode.values());
259             return;
260         }
261         webTargets.setSpecialMode(newMode);
262     }
263
264     protected void changeStreamer(boolean streamerMode) throws DaikinCommunicationException {
265         webTargets.setStreamerMode(streamerMode);
266     }
267
268     /**
269      * Updates energy year channels. Values are provided in hundreds of Watt
270      *
271      * @param channel
272      * @param maybePower
273      */
274     protected void updateEnergyYearChannel(String channel, Optional<Integer[]> maybePower) {
275         IntStream.range(1, 13).forEach(i -> updateState(
276                 String.format(DaikinBindingConstants.CHANNEL_ENERGY_STRING_FORMAT, channel, i),
277                 Objects.requireNonNull(maybePower.<State> map(
278                         t -> new QuantityType<>(BigDecimal.valueOf(t[i - 1].longValue(), 1), Units.KILOWATT_HOUR))
279                         .orElse(UnDefType.UNDEF)))
280
281         );
282     }
283
284     /**
285      *
286      * @param channel
287      * @param maybePower
288      */
289     protected void updateEnergyDayAndWeekChannel(String channel, Optional<Double> maybePower) {
290         if (maybePower.isPresent()) {
291             updateState(channel,
292                     Objects.requireNonNull(
293                             maybePower.<State> map(t -> new QuantityType<>(new DecimalType(t), Units.KILOWATT_HOUR))
294                                     .orElse(UnDefType.UNDEF)));
295         }
296     }
297
298     @Override
299     protected void registerUuid(@Nullable String key) {
300         if (key == null) {
301             return;
302         }
303         try {
304             webTargets.registerUuid(key);
305         } catch (DaikinCommunicationException e) {
306             // suppress exceptions
307             logger.debug("registerUuid({}) error: {}", key, e.getMessage());
308         }
309     }
310 }