]> git.basschouten.com Git - openhab-addons.git/blob
72ed5e5326272ca8367bfeff890590a701ec491a
[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.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
81         if (airbaseModelInfo == null || !"OK".equals(airbaseModelInfo.ret)) {
82             airbaseModelInfo = webTargets.getAirbaseModelInfo();
83             updateChannelStateDescriptions();
84         }
85
86         if (controlInfo != null) {
87             updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF);
88             updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp);
89             updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name()));
90             updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED,
91                     new StringType(controlInfo.fanSpeed.name()));
92
93             if (!controlInfo.power) {
94                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue()));
95             } else if (controlInfo.mode == AirbaseMode.COLD) {
96                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue()));
97             } else if (controlInfo.mode == AirbaseMode.HEAT) {
98                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue()));
99             } else if (controlInfo.mode == AirbaseMode.AUTO) {
100                 updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue()));
101             }
102         }
103
104         SensorInfo sensorInfo = webTargets.getAirbaseSensorInfo();
105         if (sensorInfo != null) {
106             updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp);
107
108             updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp);
109         }
110
111         AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
112         if (zoneInfo != null) {
113             IntStream.range(0, zoneInfo.zone.length)
114                     .forEach(idx -> updateState(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE + idx,
115                             OnOffType.from(zoneInfo.zone[idx])));
116         }
117         updateStatus(ThingStatus.ONLINE);
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         AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
182         long commonZones = 0;
183         long maxZones = zoneInfo.zone.length;
184
185         if (airbaseModelInfo != null) {
186             maxZones = Math.min(maxZones - 1, airbaseModelInfo.zonespresent);
187             commonZones = airbaseModelInfo.commonzone;
188         }
189         if (zone <= 0 || zone > maxZones) {
190             logger.warn("The given zone number ({}) is outside the number of zones supported by the controller ({})",
191                     zone, maxZones);
192             return;
193         }
194
195         long openZones = IntStream.range(0, zoneInfo.zone.length).filter(idx -> zoneInfo.zone[idx]).count()
196                 + commonZones;
197         logger.debug("Number of open zones: \"{}\"", openZones);
198
199         if (openZones >= 1) {
200             zoneInfo.zone[zone] = command;
201             webTargets.setAirbaseZoneInfo(zoneInfo);
202         }
203     }
204
205     @Override
206     protected void registerUuid(@Nullable String key) {
207         // not implemented. There is currently no known Airbase adapter that requires uuid authentication
208     }
209
210     protected void updateChannelStateDescriptions() {
211         updateAirbaseFanSpeedChannelStateDescription();
212         updateAirbaseModeChannelStateDescription();
213     }
214
215     protected void updateAirbaseFanSpeedChannelStateDescription() {
216         List<StateOption> options = new ArrayList<>();
217         options.add(new StateOption(AirbaseFanSpeed.AUTO.name(), AirbaseFanSpeed.AUTO.getLabel()));
218         if (airbaseModelInfo.features.contains(AirbaseFeature.AIRSIDE)) {
219             options.add(new StateOption(AirbaseFanSpeed.AIRSIDE.name(), AirbaseFanSpeed.AIRSIDE.getLabel()));
220         }
221         for (AirbaseFanSpeed f : EnumSet.range(AirbaseFanSpeed.LEVEL_1, AirbaseFanSpeed.LEVEL_5)) {
222             options.add(new StateOption(f.name(), f.getLabel()));
223         }
224         if (airbaseModelInfo.features.contains(AirbaseFeature.FRATE_AUTO)) {
225             for (AirbaseFanSpeed f : EnumSet.range(AirbaseFanSpeed.AUTO_LEVEL_1, AirbaseFanSpeed.AUTO_LEVEL_5)) {
226                 options.add(new StateOption(f.name(), f.getLabel()));
227             }
228         }
229         stateDescriptionProvider.setStateOptions(
230                 new ChannelUID(thing.getUID(), DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED), options);
231     }
232
233     protected void updateAirbaseModeChannelStateDescription() {
234         List<StateOption> options = new ArrayList<>();
235         if (airbaseModelInfo.features.contains(AirbaseFeature.AUTO)) {
236             options.add(new StateOption(AirbaseMode.AUTO.name(), AirbaseMode.AUTO.getLabel()));
237         }
238         for (AirbaseMode f : EnumSet.complementOf(EnumSet.of(AirbaseMode.AUTO, AirbaseMode.DRY))) {
239             options.add(new StateOption(f.name(), f.getLabel()));
240         }
241         if (airbaseModelInfo.features.contains(AirbaseFeature.DRY)) {
242             options.add(new StateOption(AirbaseMode.DRY.name(), AirbaseMode.DRY.getLabel()));
243         }
244         stateDescriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), DaikinBindingConstants.CHANNEL_AC_MODE),
245                 options);
246     }
247 }