]> git.basschouten.com Git - openhab-addons.git/blob
c9a3774e77632ba7c3fa4ba0010822d49af4e470
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.io.IOException;
16 import java.math.BigDecimal;
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.DaikinWebTargets;
27 import org.openhab.binding.daikin.internal.api.ControlInfo;
28 import org.openhab.binding.daikin.internal.api.EnergyInfoDayAndWeek;
29 import org.openhab.binding.daikin.internal.api.EnergyInfoYear;
30 import org.openhab.binding.daikin.internal.api.Enums.FanMovement;
31 import org.openhab.binding.daikin.internal.api.Enums.FanSpeed;
32 import org.openhab.binding.daikin.internal.api.Enums.HomekitMode;
33 import org.openhab.binding.daikin.internal.api.Enums.Mode;
34 import org.openhab.binding.daikin.internal.api.Enums.SpecialModeKind;
35 import org.openhab.binding.daikin.internal.api.SensorInfo;
36 import org.openhab.core.library.types.DecimalType;
37 import org.openhab.core.library.types.OnOffType;
38 import org.openhab.core.library.types.QuantityType;
39 import org.openhab.core.library.types.StringType;
40 import org.openhab.core.library.unit.Units;
41 import org.openhab.core.thing.ChannelUID;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingStatus;
44 import org.openhab.core.types.Command;
45 import org.openhab.core.types.State;
46 import org.openhab.core.types.UnDefType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * Handles communicating with a Daikin air conditioning unit.
52  *
53  * @author Tim Waterhouse - Initial Contribution
54  * @author Paul Smedley - Modifications to support Airbase Controllers
55  * @author Lukas Agethen - Added support for Energy Year reading, compressor frequency and powerful mode
56  * @author Wouter Denayer - Added to support for weekly & daily energy reading
57  *
58  */
59 @NonNullByDefault
60 public class DaikinAcUnitHandler extends DaikinBaseHandler {
61     private final Logger logger = LoggerFactory.getLogger(DaikinAcUnitHandler.class);
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 IOException {
70         DaikinWebTargets webTargets = this.webTargets;
71         if (webTargets == null) {
72             return;
73         }
74         ControlInfo controlInfo = webTargets.getControlInfo();
75         updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF);
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         updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE, new StringType(controlInfo.specialMode.name()));
93
94         if (controlInfo.specialMode.isUndefined()) {
95             updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL, UnDefType.UNDEF);
96         } else {
97             updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL,
98                     controlInfo.specialMode.isPowerfulActive() ? OnOffType.ON : OnOffType.OFF);
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         updateStatus(ThingStatus.ONLINE);
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) {
164                     changeFanDir(((StringType) command).toString());
165                     return true;
166                 }
167                 break;
168             case DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL:
169                 if (command instanceof OnOffType) {
170                     changeSpecialModePowerful(((OnOffType) command).equals(OnOffType.ON));
171                     return true;
172                 }
173                 break;
174         }
175         return false;
176     }
177
178     @Override
179     protected void changePower(boolean power) throws DaikinCommunicationException {
180         DaikinWebTargets webTargets = this.webTargets;
181         if (webTargets == null) {
182             return;
183         }
184         ControlInfo info = webTargets.getControlInfo();
185         info.power = power;
186         webTargets.setControlInfo(info);
187     }
188
189     @Override
190     protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException {
191         DaikinWebTargets webTargets = this.webTargets;
192         if (webTargets == null) {
193             return;
194         }
195         ControlInfo info = webTargets.getControlInfo();
196         info.temp = Optional.of(newTemperature);
197         webTargets.setControlInfo(info);
198     }
199
200     @Override
201     protected void changeMode(String mode) throws DaikinCommunicationException {
202         DaikinWebTargets webTargets = this.webTargets;
203         if (webTargets == null) {
204             return;
205         }
206         Mode newMode;
207         try {
208             newMode = Mode.valueOf(mode);
209         } catch (IllegalArgumentException ex) {
210             logger.warn("Invalid mode: {}. Valid values: {}", mode, Mode.values());
211             return;
212         }
213         ControlInfo info = webTargets.getControlInfo();
214         info.mode = newMode;
215         webTargets.setControlInfo(info);
216     }
217
218     @Override
219     protected void changeFanSpeed(String fanSpeed) throws DaikinCommunicationException {
220         DaikinWebTargets webTargets = this.webTargets;
221         if (webTargets == null) {
222             return;
223         }
224         FanSpeed newSpeed;
225         try {
226             newSpeed = FanSpeed.valueOf(fanSpeed);
227         } catch (IllegalArgumentException ex) {
228             logger.warn("Invalid fan speed: {}. Valid values: {}", fanSpeed, FanSpeed.values());
229             return;
230         }
231         ControlInfo info = webTargets.getControlInfo();
232         info.fanSpeed = newSpeed;
233         webTargets.setControlInfo(info);
234     }
235
236     protected void changeFanDir(String fanDir) throws DaikinCommunicationException {
237         DaikinWebTargets webTargets = this.webTargets;
238         if (webTargets == null) {
239             return;
240         }
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     /**
254      *
255      * @param powerfulMode
256      * @return Is change successful
257      * @throws DaikinCommunicationException
258      */
259     protected boolean changeSpecialModePowerful(boolean powerfulMode) throws DaikinCommunicationException {
260         DaikinWebTargets webTargets = this.webTargets;
261         if (webTargets == null) {
262             return false;
263         }
264         return webTargets.setSpecialMode(SpecialModeKind.POWERFUL, powerfulMode);
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             DaikinWebTargets webTargets = this.webTargets;
303             if (webTargets == null) {
304                 return;
305             }
306             webTargets.registerUuid(key);
307         } catch (DaikinCommunicationException e) {
308             // suppress exceptions
309             logger.debug("registerUuid({}) error: {}", key, e.getMessage());
310         }
311     }
312 }