2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.daikin.internal.handler;
15 import java.io.IOException;
16 import java.math.BigDecimal;
17 import java.util.Optional;
18 import java.util.stream.IntStream;
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.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.SpecialModeKind;
34 import org.openhab.binding.daikin.internal.api.SensorInfo;
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.SmartHomeUnits;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
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;
49 * Handles communicating with a Daikin air conditioning unit.
51 * @author Tim Waterhouse - Initial Contribution
52 * @author Paul Smedley <paul@smedley.id.au> - Modifications to support Airbase Controllers
53 * @author Lukas Agethen - Added support for Energy Year reading, compressor frequency and powerful mode
56 public class DaikinAcUnitHandler extends DaikinBaseHandler {
57 private final Logger logger = LoggerFactory.getLogger(DaikinAcUnitHandler.class);
59 public DaikinAcUnitHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider,
60 @Nullable HttpClient httpClient) {
61 super(thing, stateDescriptionProvider, httpClient);
65 protected void pollStatus() throws IOException {
66 DaikinWebTargets webTargets = this.webTargets;
67 if (webTargets == null) {
70 ControlInfo controlInfo = webTargets.getControlInfo();
71 updateStatus(ThingStatus.ONLINE);
72 updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF);
73 updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
75 updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name()));
76 updateState(DaikinBindingConstants.CHANNEL_AC_FAN_SPEED, new StringType(controlInfo.fanSpeed.name()));
77 updateState(DaikinBindingConstants.CHANNEL_AC_FAN_DIR, new StringType(controlInfo.fanMovement.name()));
79 if (!controlInfo.power) {
80 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue()));
81 } else if (controlInfo.mode == Mode.COLD) {
82 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue()));
83 } else if (controlInfo.mode == Mode.HEAT) {
84 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue()));
85 } else if (controlInfo.mode == Mode.AUTO) {
86 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue()));
89 updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE, new StringType(controlInfo.specialMode.name()));
91 if (controlInfo.specialMode.isUndefined()) {
92 updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL, UnDefType.UNDEF);
94 updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL,
95 controlInfo.specialMode.isPowerfulActive() ? OnOffType.ON : OnOffType.OFF);
98 SensorInfo sensorInfo = webTargets.getSensorInfo();
99 updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp);
101 updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp);
103 if (sensorInfo.indoorhumidity.isPresent()) {
104 updateState(DaikinBindingConstants.CHANNEL_HUMIDITY,
105 new QuantityType<>(sensorInfo.indoorhumidity.get(), SmartHomeUnits.PERCENT));
107 updateState(DaikinBindingConstants.CHANNEL_HUMIDITY, UnDefType.UNDEF);
110 if (sensorInfo.compressorfrequency.isPresent()) {
111 updateState(DaikinBindingConstants.CHANNEL_CMP_FREQ,
112 new QuantityType<>(sensorInfo.compressorfrequency.get(), SmartHomeUnits.PERCENT));
114 updateState(DaikinBindingConstants.CHANNEL_CMP_FREQ, UnDefType.UNDEF);
118 EnergyInfoYear energyInfoYear = webTargets.getEnergyInfoYear();
120 if (energyInfoYear.energyHeatingThisYear.isPresent()) {
121 updateEnergyYearChannel(DaikinBindingConstants.CHANNEL_ENERGY_HEATING_CURRENTYEAR_PREFIX,
122 energyInfoYear.energyHeatingThisYear);
124 if (energyInfoYear.energyCoolingThisYear.isPresent()) {
125 updateEnergyYearChannel(DaikinBindingConstants.CHANNEL_ENERGY_COOLING_CURRENTYEAR_PREFIX,
126 energyInfoYear.energyCoolingThisYear);
128 } catch (DaikinCommunicationException e) {
129 // Suppress any error if energy info is not supported.
130 logger.debug("getEnergyInfoYear() error: {}", e.getMessage());
135 protected boolean handleCommandInternal(ChannelUID channelUID, Command command)
136 throws DaikinCommunicationException {
137 switch (channelUID.getId()) {
138 case DaikinBindingConstants.CHANNEL_AC_FAN_DIR:
139 if (command instanceof StringType) {
140 changeFanDir(((StringType) command).toString());
144 case DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL:
145 if (command instanceof OnOffType) {
146 changeSpecialModePowerful(((OnOffType) command).equals(OnOffType.ON));
155 protected void changePower(boolean power) throws DaikinCommunicationException {
156 DaikinWebTargets webTargets = this.webTargets;
157 if (webTargets == null) {
160 ControlInfo info = webTargets.getControlInfo();
162 webTargets.setControlInfo(info);
166 protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException {
167 DaikinWebTargets webTargets = this.webTargets;
168 if (webTargets == null) {
171 ControlInfo info = webTargets.getControlInfo();
172 info.temp = Optional.of(newTemperature);
173 webTargets.setControlInfo(info);
177 protected void changeMode(String mode) throws DaikinCommunicationException {
178 DaikinWebTargets webTargets = this.webTargets;
179 if (webTargets == null) {
184 newMode = Mode.valueOf(mode);
185 } catch (IllegalArgumentException ex) {
186 logger.warn("Invalid mode: {}. Valid values: {}", mode, Mode.values());
189 ControlInfo info = webTargets.getControlInfo();
191 webTargets.setControlInfo(info);
195 protected void changeFanSpeed(String fanSpeed) throws DaikinCommunicationException {
196 DaikinWebTargets webTargets = this.webTargets;
197 if (webTargets == null) {
202 newSpeed = FanSpeed.valueOf(fanSpeed);
203 } catch (IllegalArgumentException ex) {
204 logger.warn("Invalid fan speed: {}. Valid values: {}", fanSpeed, FanSpeed.values());
207 ControlInfo info = webTargets.getControlInfo();
208 info.fanSpeed = newSpeed;
209 webTargets.setControlInfo(info);
212 protected void changeFanDir(String fanDir) throws DaikinCommunicationException {
213 DaikinWebTargets webTargets = this.webTargets;
214 if (webTargets == null) {
217 FanMovement newMovement;
219 newMovement = FanMovement.valueOf(fanDir);
220 } catch (IllegalArgumentException ex) {
221 logger.warn("Invalid fan direction: {}. Valid values: {}", fanDir, FanMovement.values());
224 ControlInfo info = webTargets.getControlInfo();
225 info.fanMovement = newMovement;
226 webTargets.setControlInfo(info);
231 * @param powerfulMode
232 * @return Is change successful
233 * @throws DaikinCommunicationException
235 protected boolean changeSpecialModePowerful(boolean powerfulMode) throws DaikinCommunicationException {
236 DaikinWebTargets webTargets = this.webTargets;
237 if (webTargets == null) {
240 return webTargets.setSpecialMode(SpecialModeKind.POWERFUL, powerfulMode);
244 * Updates energy year channels. Values are provided in hundreds of Watt
246 * @param channelPrefix
249 protected void updateEnergyYearChannel(String channelPrefix, Optional<Integer[]> maybePower) {
250 IntStream.range(1, 13)
251 .forEach(i -> updateState(
252 String.format(DaikinBindingConstants.CHANNEL_ENERGY_STRING_FORMAT, channelPrefix, i),
253 maybePower.<State> map(t -> new QuantityType<>(BigDecimal.valueOf(t[i - 1].longValue(), 1),
254 SmartHomeUnits.KILOWATT_HOUR)).orElse(UnDefType.UNDEF))
260 protected void registerUuid(@Nullable String key) {
265 DaikinWebTargets webTargets = this.webTargets;
266 if (webTargets == null) {
269 webTargets.registerUuid(key);
270 } catch (Exception e) {
271 // suppress exceptions
272 logger.debug("registerUuid({}) error: {}", key, e.getMessage());