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