]> git.basschouten.com Git - openhab-addons.git/blob
5acbffa665dfaae29f74574e3907bf2ca1423ca1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.util.ArrayList;
17 import java.util.EnumSet;
18 import java.util.List;
19 import java.util.Optional;
20 import java.util.stream.IntStream;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.openhab.binding.daikin.internal.DaikinBindingConstants;
26 import org.openhab.binding.daikin.internal.DaikinCommunicationException;
27 import org.openhab.binding.daikin.internal.DaikinDynamicStateDescriptionProvider;
28 import org.openhab.binding.daikin.internal.api.Enums.HomekitMode;
29 import org.openhab.binding.daikin.internal.api.SensorInfo;
30 import org.openhab.binding.daikin.internal.api.airbase.AirbaseControlInfo;
31 import org.openhab.binding.daikin.internal.api.airbase.AirbaseEnums.AirbaseFanSpeed;
32 import org.openhab.binding.daikin.internal.api.airbase.AirbaseEnums.AirbaseFeature;
33 import org.openhab.binding.daikin.internal.api.airbase.AirbaseEnums.AirbaseMode;
34 import org.openhab.binding.daikin.internal.api.airbase.AirbaseModelInfo;
35 import org.openhab.binding.daikin.internal.api.airbase.AirbaseZoneInfo;
36 import org.openhab.core.library.types.OnOffType;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.StateOption;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * Handles communicating with a Daikin Airbase wifi adapter.
48  *
49  * @author Tim Waterhouse - Initial Contribution
50  * @author Paul Smedley <paul@smedley.id.au> - Modifications to support Airbase Controllers
51  * @author Jimmy Tanagra - Support Airside and auto fan levels, DynamicStateDescription
52  *
53  */
54 @NonNullByDefault
55 public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
56     private final Logger logger = LoggerFactory.getLogger(DaikinAirbaseUnitHandler.class);
57     private @Nullable AirbaseModelInfo airbaseModelInfo;
58
59     public DaikinAirbaseUnitHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider,
60             @Nullable HttpClient httpClient) {
61         super(thing, stateDescriptionProvider, httpClient);
62     }
63
64     @Override
65     protected boolean handleCommandInternal(ChannelUID channelUID, Command command)
66             throws DaikinCommunicationException {
67         if (channelUID.getId().startsWith(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE)) {
68             int zoneNumber = Integer.parseInt(channelUID.getId().substring(4));
69             if (command instanceof OnOffType) {
70                 changeZone(zoneNumber, command == OnOffType.ON);
71                 return true;
72             }
73         }
74         return false;
75     }
76
77     @Override
78     protected void pollStatus() throws IOException {
79         AirbaseControlInfo controlInfo = webTargets.getAirbaseControlInfo();
80         updateStatus(ThingStatus.ONLINE);
81
82         if (airbaseModelInfo == null || !"OK".equals(airbaseModelInfo.ret)) {
83             airbaseModelInfo = webTargets.getAirbaseModelInfo();
84             updateChannelStateDescriptions();
85         }
86
87         if (controlInfo != null) {
88             updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF);
89             updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
90             updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name()));
91             updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED,
92                     new StringType(controlInfo.fanSpeed.name()));
93
94             if (!controlInfo.power) {
95                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue()));
96             } else if (controlInfo.mode == AirbaseMode.COLD) {
97                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue()));
98             } else if (controlInfo.mode == AirbaseMode.HEAT) {
99                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue()));
100             } else if (controlInfo.mode == AirbaseMode.AUTO) {
101                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue()));
102             }
103         }
104
105         SensorInfo sensorInfo = webTargets.getAirbaseSensorInfo();
106         if (sensorInfo != null) {
107             updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp);
108
109             updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp);
110         }
111
112         AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
113         if (zoneInfo != null) {
114             IntStream.range(0, zoneInfo.zone.length)
115                     .forEach(idx -> updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE + idx,
116                             OnOffType.from(zoneInfo.zone[idx])));
117         }
118     }
119
120     @Override
121     protected void changePower(boolean power) throws DaikinCommunicationException {
122         AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
123         info.power = power;
124         webTargets.setAirbaseControlInfo(info);
125     }
126
127     @Override
128     protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException {
129         AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
130         info.temp = Optional.of(newTemperature);
131         webTargets.setAirbaseControlInfo(info);
132     }
133
134     @Override
135     protected void changeMode(String mode) throws DaikinCommunicationException {
136         AirbaseMode newMode;
137         try {
138             newMode = AirbaseMode.valueOf(mode);
139         } catch (IllegalArgumentException ex) {
140             logger.warn("Invalid mode: {}. Valid values: {}", mode, AirbaseMode.values());
141             return;
142         }
143         if (airbaseModelInfo != null) {
144             if ((newMode == AirbaseMode.AUTO && !airbaseModelInfo.features.contains(AirbaseFeature.AUTO))
145                     || (newMode == AirbaseMode.DRY && !airbaseModelInfo.features.contains(AirbaseFeature.DRY))) {
146                 logger.warn("{} mode is not supported by your controller", mode);
147                 return;
148             }
149         }
150         AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
151         info.mode = newMode;
152         webTargets.setAirbaseControlInfo(info);
153     }
154
155     @Override
156     protected void changeFanSpeed(String speed) throws DaikinCommunicationException {
157         AirbaseFanSpeed newFanSpeed;
158         try {
159             newFanSpeed = AirbaseFanSpeed.valueOf(speed);
160         } catch (IllegalArgumentException ex) {
161             logger.warn("Invalid fan speed: {}. Valid values: {}", speed, AirbaseFanSpeed.values());
162             return;
163         }
164         if (airbaseModelInfo != null) {
165             if (EnumSet.range(AirbaseFanSpeed.AUTO_LEVEL_1, AirbaseFanSpeed.AUTO_LEVEL_5).contains(newFanSpeed)
166                     && !airbaseModelInfo.features.contains(AirbaseFeature.FRATE_AUTO)) {
167                 logger.warn("Fan AUTO_LEVEL_X is not supported by your controller");
168                 return;
169             }
170             if (newFanSpeed == AirbaseFanSpeed.AIRSIDE && !airbaseModelInfo.features.contains(AirbaseFeature.AIRSIDE)) {
171                 logger.warn("Airside is not supported by your controller");
172                 return;
173             }
174         }
175         AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
176         info.fanSpeed = newFanSpeed;
177         webTargets.setAirbaseControlInfo(info);
178     }
179
180     protected void changeZone(int zone, boolean command) throws DaikinCommunicationException {
181         if (zone <= 0 || zone > airbaseModelInfo.zonespresent) {
182             logger.warn("The given zone number ({}) is outside the number of zones supported by the controller ({})",
183                     zone, airbaseModelInfo.zonespresent);
184             return;
185         }
186
187         AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
188         long count = IntStream.range(0, zoneInfo.zone.length).filter(idx -> zoneInfo.zone[idx]).count()
189                 + airbaseModelInfo.commonzone;
190         logger.debug("Number of open zones: \"{}\"", count);
191
192         if (count >= 1) {
193             zoneInfo.zone[zone] = command;
194             webTargets.setAirbaseZoneInfo(zoneInfo);
195         }
196     }
197
198     @Override
199     protected void registerUuid(@Nullable String key) {
200         // not implemented. There is currently no known Airbase adapter that requires uuid authentication
201     }
202
203     protected void updateChannelStateDescriptions() {
204         updateAirbaseFanSpeedChannelStateDescription();
205         updateAirbaseModeChannelStateDescription();
206     }
207
208     protected void updateAirbaseFanSpeedChannelStateDescription() {
209         List<StateOption> options = new ArrayList<>();
210         options.add(new StateOption(AirbaseFanSpeed.AUTO.name(), AirbaseFanSpeed.AUTO.getLabel()));
211         if (airbaseModelInfo.features.contains(AirbaseFeature.AIRSIDE)) {
212             options.add(new StateOption(AirbaseFanSpeed.AIRSIDE.name(), AirbaseFanSpeed.AIRSIDE.getLabel()));
213         }
214         for (AirbaseFanSpeed f : EnumSet.range(AirbaseFanSpeed.LEVEL_1, AirbaseFanSpeed.LEVEL_5)) {
215             options.add(new StateOption(f.name(), f.getLabel()));
216         }
217         if (airbaseModelInfo.features.contains(AirbaseFeature.FRATE_AUTO)) {
218             for (AirbaseFanSpeed f : EnumSet.range(AirbaseFanSpeed.AUTO_LEVEL_1, AirbaseFanSpeed.AUTO_LEVEL_5)) {
219                 options.add(new StateOption(f.name(), f.getLabel()));
220             }
221         }
222         stateDescriptionProvider.setStateOptions(
223                 new ChannelUID(thing.getUID(), DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED), options);
224     }
225
226     protected void updateAirbaseModeChannelStateDescription() {
227         List<StateOption> options = new ArrayList<>();
228         if (airbaseModelInfo.features.contains(AirbaseFeature.AUTO)) {
229             options.add(new StateOption(AirbaseMode.AUTO.name(), AirbaseMode.AUTO.getLabel()));
230         }
231         for (AirbaseMode f : EnumSet.complementOf(EnumSet.of(AirbaseMode.AUTO, AirbaseMode.DRY))) {
232             options.add(new StateOption(f.name(), f.getLabel()));
233         }
234         if (airbaseModelInfo.features.contains(AirbaseFeature.DRY)) {
235             options.add(new StateOption(AirbaseMode.DRY.name(), AirbaseMode.DRY.getLabel()));
236         }
237         stateDescriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), DaikinBindingConstants.CHANNEL_AC_MODE),
238                 options);
239     }
240 }