From 159d553468731e4a8c2abc02fa1fa001858af9ed Mon Sep 17 00:00:00 2001 From: LukasA83 <58861945+LukasA83@users.noreply.github.com> Date: Sat, 23 May 2020 19:21:24 +0200 Subject: [PATCH] [daikin] Add Energy, compressor frequency and special mode channels (#7708) * [daikin] Add Energy, compressor frequency and special mode channels #6980: Add compressor frequency channel #6115: Add energy current year channels * Update small typos and code refactoring as suggested by cpmeister * Update small typos and added units where possible Signed-off-by: Lukas Agethen --- bundles/org.openhab.binding.daikin/README.md | 27 +++ .../internal/DaikinBindingConstants.java | 9 + .../DaikinCommunicationException.java | 2 +- ...DaikinCommunicationForbiddenException.java | 4 +- .../daikin/internal/DaikinWebTargets.java | 32 ++- .../daikin/internal/api/BasicInfo.java | 3 - .../daikin/internal/api/ControlInfo.java | 5 +- .../daikin/internal/api/EnergyInfoYear.java | 53 +++++ .../binding/daikin/internal/api/Enums.java | 73 ++++++- .../daikin/internal/api/InfoParser.java | 40 +++- .../daikin/internal/api/SensorInfo.java | 4 +- .../api/airbase/AirbaseModelInfo.java | 15 +- .../DaikinACUnitDiscoveryService.java | 4 - .../internal/handler/DaikinAcUnitHandler.java | 159 +++++++++++--- .../internal/handler/DaikinBaseHandler.java | 13 +- .../resources/ESH-INF/thing/thing-types.xml | 200 +++++++++++++++++- 16 files changed, 568 insertions(+), 75 deletions(-) create mode 100644 bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/EnergyInfoYear.java diff --git a/bundles/org.openhab.binding.daikin/README.md b/bundles/org.openhab.binding.daikin/README.md index 16c41851d0d55..caeff8f2315d5 100644 --- a/bundles/org.openhab.binding.daikin/README.md +++ b/bundles/org.openhab.binding.daikin/README.md @@ -45,6 +45,33 @@ For the BRP072A42 and BRP072C42: | homekitmode | A mode that is compatible with homekit/alexa/google home (off, auto, heat, cool). Not tested for BRP069B41 | | fanspeed | The fan speed set for the unit (AUTO, SILENCE, LEVEL_1, LEVEL_2, LEVEL_3, LEVEL_4, LEVEL_5) | | fandir | The fan blade direction (STOPPED, VERTICAL, HORIZONTAL, VERTICAL_AND_HORIZONTAL) | +| cmpfrequency | The compressor frequency | +| specialmode | The special mode currently active (OFF, STREAMER, ECO, POWERFUL, POWERFUL & STREAMER, ECO & STREAMER). Not all modes might be available on the unit. | +| specialmode-powerful | Turns the powerful mode on/off for the air conditioning unit. | +| energyheatingcurrentyear-1 | The energy consumption when heating for current year January | +| energyheatingcurrentyear-2 | The energy consumption when heating for current year February | +| energyheatingcurrentyear-3 | The energy consumption when heating for current year March | +| energyheatingcurrentyear-4 | The energy consumption when heating for current year April | +| energyheatingcurrentyear-5 | The energy consumption when heating for current year May | +| energyheatingcurrentyear-6 | The energy consumption when heating for current year June | +| energyheatingcurrentyear-7 | The energy consumption when heating for current year July | +| energyheatingcurrentyear-8 | The energy consumption when heating for current year August | +| energyheatingcurrentyear-9 | The energy consumption when heating for current year September | +| energyheatingcurrentyear-10| The energy consumption when heating for current year October | +| energyheatingcurrentyear-11| The energy consumption when heating for current year November | +| energyheatingcurrentyear-12| The energy consumption when heating for current year December | +| energycoolingcurrentyear-1 | The energy consumption when cooling for current year January | +| energycoolingcurrentyear-2 | The energy consumption when cooling for current year February | +| energycoolingcurrentyear-3 | The energy consumption when cooling for current year March | +| energycoolingcurrentyear-4 | The energy consumption when cooling for current year April | +| energycoolingcurrentyear-5 | The energy consumption when cooling for current year May | +| energycoolingcurrentyear-6 | The energy consumption when cooling for current year June | +| energycoolingcurrentyear-7 | The energy consumption when cooling for current year July | +| energycoolingcurrentyear-8 | The energy consumption when cooling for current year August | +| energycoolingcurrentyear-9 | The energy consumption when cooling for current year September | +| energycoolingcurrentyear-10| The energy consumption when cooling for current year October | +| energycoolingcurrentyear-11| The energy consumption when cooling for current year November | +| energycoolingcurrentyear-12| The energy consumption when cooling for current year December | For the BRP15B61: diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java index ce3935a552081..659fcb9827203 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinBindingConstants.java @@ -46,6 +46,15 @@ public class DaikinBindingConstants { public static final String CHANNEL_AC_FAN_SPEED = "fanspeed"; public static final String CHANNEL_AC_FAN_DIR = "fandir"; public static final String CHANNEL_HUMIDITY = "humidity"; + public static final String CHANNEL_CMP_FREQ = "cmpfrequency"; + + // Prefix and channel id format for energy - currentyear + public static final String CHANNEL_ENERGY_HEATING_CURRENTYEAR_PREFIX = "energyheatingcurrentyear"; + public static final String CHANNEL_ENERGY_COOLING_CURRENTYEAR_PREFIX = "energycoolingcurrentyear"; + public static final String CHANNEL_ENERGY_STRING_FORMAT = "%s-%d"; + + public static final String CHANNEL_AC_SPECIALMODE = "specialmode"; + public static final String CHANNEL_AC_SPECIALMODE_POWERFUL = "specialmode-powerful"; // additional channels for Airbase Controller public static final String CHANNEL_AIRBASE_AC_FAN_SPEED = "airbasefanspeed"; diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationException.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationException.java index 538398e8519c5..4d4c011533729 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationException.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationException.java @@ -13,8 +13,8 @@ package org.openhab.binding.daikin.internal; import java.io.IOException; + import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** * Exception for when an unexpected response is received from the Daikin controller. diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationForbiddenException.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationForbiddenException.java index 44e2aea1ef7c8..7424ce89625bd 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationForbiddenException.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinCommunicationForbiddenException.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.daikin.internal; -import java.io.IOException; import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -23,6 +22,9 @@ */ @NonNullByDefault public class DaikinCommunicationForbiddenException extends DaikinCommunicationException { + + private static final long serialVersionUID = 1L; + public DaikinCommunicationForbiddenException(String message) { super(message); } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinWebTargets.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinWebTargets.java index 793f9614fd448..6b86c43d736f6 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinWebTargets.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/DaikinWebTargets.java @@ -13,11 +13,10 @@ package org.openhab.binding.daikin.internal; import java.io.IOException; -import java.util.concurrent.TimeUnit; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -29,7 +28,9 @@ import org.eclipse.smarthome.io.net.http.HttpUtil; import org.openhab.binding.daikin.internal.api.BasicInfo; import org.openhab.binding.daikin.internal.api.ControlInfo; +import org.openhab.binding.daikin.internal.api.Enums.SpecialModeKind; import org.openhab.binding.daikin.internal.api.SensorInfo; +import org.openhab.binding.daikin.internal.api.EnergyInfoYear; import org.openhab.binding.daikin.internal.api.airbase.AirbaseBasicInfo; import org.openhab.binding.daikin.internal.api.airbase.AirbaseControlInfo; import org.openhab.binding.daikin.internal.api.airbase.AirbaseModelInfo; @@ -37,7 +38,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** * Handles performing the actual HTTP requests for communicating with Daikin air conditioning units. * @@ -55,6 +55,9 @@ public class DaikinWebTargets { private String getControlInfoUri; private String getSensorInfoUri; private String registerUuidUri; + private String getEnergyInfoYearUri; + private String setSpecialModeUri; + private String setAirbaseControlInfoUri; private String getAirbaseControlInfoUri; private String getAirbaseSensorInfoUri; @@ -68,7 +71,8 @@ public class DaikinWebTargets { private Logger logger = LoggerFactory.getLogger(DaikinWebTargets.class); - public DaikinWebTargets(@Nullable HttpClient httpClient, @Nullable String host, @Nullable Boolean secure, @Nullable String uuid) { + public DaikinWebTargets(@Nullable HttpClient httpClient, @Nullable String host, @Nullable Boolean secure, + @Nullable String uuid) { this.httpClient = httpClient; this.uuid = uuid; @@ -78,6 +82,8 @@ public DaikinWebTargets(@Nullable HttpClient httpClient, @Nullable String host, getControlInfoUri = baseUri + "aircon/get_control_info"; getSensorInfoUri = baseUri + "aircon/get_sensor_info"; registerUuidUri = baseUri + "common/register_terminal"; + getEnergyInfoYearUri = baseUri + "aircon/get_year_power_ex"; + setSpecialModeUri = baseUri + "aircon/set_special_mode"; // Daikin Airbase API getAirbaseBasicInfoUri = baseUri + "skyfi/common/basic_info"; @@ -117,6 +123,19 @@ public void registerUuid(String key) throws DaikinCommunicationException { logger.debug("registerUuid result: {}", response); } + public EnergyInfoYear getEnergyInfoYear() throws DaikinCommunicationException { + String response = invoke(getEnergyInfoYearUri); + return EnergyInfoYear.parse(response); + } + + public boolean setSpecialMode(SpecialModeKind specialModeKind, boolean state) throws DaikinCommunicationException { + Map queryParams = new HashMap<>(); + queryParams.put("spmode_kind",String.valueOf(specialModeKind.getValue())); + queryParams.put("set_spmode", state ? "1" : "0"); + String response = invoke(setSpecialModeUri, queryParams); + return !response.contains("ret=OK"); + } + // Daikin Airbase API public AirbaseControlInfo getAirbaseControlInfo() throws DaikinCommunicationException { String response = invoke(getAirbaseControlInfoUri); @@ -188,9 +207,8 @@ private String invoke(String uri, Map params) throws DaikinCommu private String executeUrl(String url) throws DaikinCommunicationException { try { - Request request = httpClient.newRequest(url) - .method(HttpMethod.GET) - .timeout(TIMEOUT_MS, TimeUnit.MILLISECONDS); + Request request = httpClient.newRequest(url).method(HttpMethod.GET).timeout(TIMEOUT_MS, + TimeUnit.MILLISECONDS); if (uuid != null) { request.header("X-Daikin-uuid", uuid); logger.debug("Header: X-Daikin-uuid: {}", uuid); diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/BasicInfo.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/BasicInfo.java index 397df7374204f..01846df059dda 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/BasicInfo.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/BasicInfo.java @@ -17,9 +17,6 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.daikin.internal.api.InfoParser; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/ControlInfo.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/ControlInfo.java index e82c99b356ce1..9ead5b570a998 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/ControlInfo.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/ControlInfo.java @@ -17,10 +17,10 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; - import org.openhab.binding.daikin.internal.api.Enums.FanMovement; import org.openhab.binding.daikin.internal.api.Enums.FanSpeed; import org.openhab.binding.daikin.internal.api.Enums.Mode; +import org.openhab.binding.daikin.internal.api.Enums.SpecialMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +44,7 @@ public class ControlInfo { public FanMovement fanMovement = FanMovement.STOPPED; /* Not supported by all units. Sets the target humidity for dehumidifying. */ public Optional targetHumidity = Optional.empty(); + public SpecialMode specialMode = SpecialMode.UNKNOWN; private ControlInfo() { } @@ -65,6 +66,8 @@ public static ControlInfo parse(String response) { .map(value -> FanMovement.fromValue(value)).orElse(FanMovement.STOPPED); info.targetHumidity = Optional.ofNullable(responseMap.get("shum")).flatMap(value -> InfoParser.parseInt(value)); + info.specialMode = Optional.ofNullable(responseMap.get("adv")).map(value -> SpecialMode.fromValue(value)) + .orElse(SpecialMode.UNKNOWN); return info; } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/EnergyInfoYear.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/EnergyInfoYear.java new file mode 100644 index 0000000000000..009e2a1e59180 --- /dev/null +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/EnergyInfoYear.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2020 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.daikin.internal.api; + +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Holds information from the get_year_power_ex call. + * + * @author Lukas Agethen - Initial contribution + * + */ +@NonNullByDefault +public class EnergyInfoYear { + private static final Logger logger = LoggerFactory.getLogger(EnergyInfoYear.class); + + public Optional energyHeatingThisYear = Optional.empty(); + + public Optional energyCoolingThisYear = Optional.empty(); + + private EnergyInfoYear() { + } + + public static EnergyInfoYear parse(String response) { + logger.debug("Parsing string: \"{}\"", response); + + Map responseMap = InfoParser.parse(response); + + EnergyInfoYear info = new EnergyInfoYear(); + info.energyHeatingThisYear = Optional.ofNullable(responseMap.get("curr_year_heat")) + .flatMap(value -> InfoParser.parseArrayofInt(value, 12)); + + info.energyCoolingThisYear = Optional.ofNullable(responseMap.get("curr_year_cool")) + .flatMap(value -> InfoParser.parseArrayofInt(value, 12)); + + return info; + } +} diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/Enums.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/Enums.java index 6dddcecb114c9..a3e36648b78da 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/Enums.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/Enums.java @@ -13,7 +13,6 @@ package org.openhab.binding.daikin.internal.api; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,6 +20,7 @@ * Container class for enums related to Daikin A/C systems * * @author Tim Waterhouse - Initial contribution + * @author Lukas Agethen - Add special modes * */ @NonNullByDefault @@ -138,4 +138,75 @@ public String getValue() { return value; } } + + public enum SpecialMode { + STREAMER("13"), + ECO("12"), + POWERFUL("2"), + POWERFUL_STREAMER("2/13"), + ECO_STREAMER("12/13"), + OFF(""), + UNKNOWN("??"); + + private static final Logger LOGGER = LoggerFactory.getLogger(SpecialMode.class); + private final String value; + + SpecialMode(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public boolean isPowerfulActive() { + return this.equals(POWERFUL) || this.equals(POWERFUL_STREAMER); + } + + public boolean isUndefined() { + return this.equals(UNKNOWN); + } + + public static SpecialMode fromValue(String value) { + for (SpecialMode m : SpecialMode.values()) { + if (m.getValue().equals(value)) { + return m; + } + } + LOGGER.debug("Unexpected SpecialMode value of \"{}\"", value); + + // Default to UNKNOWN + return UNKNOWN; + } + } + + public enum SpecialModeKind { + UNKNOWN(-1), + STREAMER(0), + POWERFUL(1), + ECO(2); + + private static final Logger LOGGER = LoggerFactory.getLogger(SpecialModeKind.class); + private final int value; + + SpecialModeKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static SpecialModeKind fromValue(int value) { + for (SpecialModeKind m : SpecialModeKind.values()) { + if (m.getValue() == value) { + return m; + } + } + LOGGER.debug("Unexpected SpecialModeKind value of \"{}\"", value); + + // Default to UNKNOWN + return UNKNOWN; + } + } } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/InfoParser.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/InfoParser.java index c90de0de2060c..65e2d099bfd83 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/InfoParser.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/InfoParser.java @@ -12,16 +12,15 @@ */ package org.openhab.binding.daikin.internal.api; -import java.util.Arrays; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Class for parsing the comma separated values returned by the Daikin Controller. + * Class for parsing the comma separated values and array values returned by the Daikin Controller. * * @author Jimmy Tanagra - Initial Contribution * @@ -32,13 +31,12 @@ private InfoParser() { } public static Map parse(String response) { - return Arrays.asList(response.split(",")).stream().filter(kv -> kv.contains("=")) - .map(kv -> { - String[] keyValue = kv.split("="); - String key = keyValue[0]; - String value = keyValue.length > 1 ? keyValue[1] : ""; - return new String[] { key, value }; - }).collect(Collectors.toMap(x -> x[0], x -> x[1])); + return Stream.of(response.split(",")).filter(kv -> kv.contains("=")).map(kv -> { + String[] keyValue = kv.split("="); + String key = keyValue[0]; + String value = keyValue.length > 1 ? keyValue[1] : ""; + return new String[] { key, value }; + }).collect(Collectors.toMap(x -> x[0], x -> x[1])); } public static Optional parseDouble(String value) { @@ -58,8 +56,28 @@ public static Optional parseInt(String value) { } try { return Optional.of(Integer.parseInt(value)); - } catch (Exception e) { + } catch (NumberFormatException e) { + return Optional.empty(); + } + } + + public static Optional parseArrayofInt(String value) { + if ("-".equals(value)) { + return Optional.empty(); + } + try { + return Optional.of(Stream.of(value.split("/")).map(val -> Integer.parseInt(val)).toArray(Integer[]::new)); + + } catch (NumberFormatException e) { return Optional.empty(); } } + + public static Optional parseArrayofInt(String value, int expectedArraySize) { + Optional result = parseArrayofInt(value); + if (result.isPresent() && result.get().length == expectedArraySize) { + return result; + } + return Optional.empty(); + } } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/SensorInfo.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/SensorInfo.java index 5bc453b0c69ff..80ce9bac04bcb 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/SensorInfo.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/SensorInfo.java @@ -31,6 +31,7 @@ public class SensorInfo { public Optional indoortemp = Optional.empty(); public Optional indoorhumidity = Optional.empty(); public Optional outdoortemp = Optional.empty(); + public Optional compressorfrequency = Optional.empty(); private SensorInfo() { } @@ -44,7 +45,8 @@ public static SensorInfo parse(String response) { info.indoortemp = Optional.ofNullable(responseMap.get("htemp")).flatMap(value -> InfoParser.parseDouble(value)); info.indoorhumidity = Optional.ofNullable(responseMap.get("hhum")).flatMap(value -> InfoParser.parseDouble(value)); info.outdoortemp = Optional.ofNullable(responseMap.get("otemp")).flatMap(value -> InfoParser.parseDouble(value)); - + info.compressorfrequency = Optional.ofNullable(responseMap.get("cmpfreq")).flatMap(value -> InfoParser.parseDouble(value)); + return info; } } diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/airbase/AirbaseModelInfo.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/airbase/AirbaseModelInfo.java index e861f2369bccb..fc6f2a297e201 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/airbase/AirbaseModelInfo.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/api/airbase/AirbaseModelInfo.java @@ -12,18 +12,16 @@ */ package org.openhab.binding.daikin.internal.api.airbase; -import java.util.Map; import java.util.EnumSet; +import java.util.Map; import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.daikin.internal.api.InfoParser; import org.openhab.binding.daikin.internal.api.airbase.AirbaseEnums.AirbaseFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.openhab.binding.daikin.internal.api.InfoParser; -import org.openhab.binding.daikin.internal.api.airbase.AirbaseEnums.AirbaseFeature; - /** * Class for holding the set of parameters used by get model info. * @@ -51,9 +49,12 @@ public static AirbaseModelInfo parse(String response) { AirbaseModelInfo info = new AirbaseModelInfo(); info.ret = Optional.ofNullable(responseMap.get("ret")).orElse(""); - info.zonespresent = Optional.ofNullable(responseMap.get("en_zone")).flatMap(value -> InfoParser.parseInt(value)).orElse(0); - info.commonzone = Optional.ofNullable(responseMap.get("en_common_zone")).flatMap(value -> InfoParser.parseInt(value)).orElse(0); - info.frate_steps = Optional.ofNullable(responseMap.get("frate_steps")).flatMap(value -> InfoParser.parseInt(value)).orElse(1); + info.zonespresent = Optional.ofNullable(responseMap.get("en_zone")).flatMap(value -> InfoParser.parseInt(value)) + .orElse(0); + info.commonzone = Optional.ofNullable(responseMap.get("en_common_zone")) + .flatMap(value -> InfoParser.parseInt(value)).orElse(0); + info.frate_steps = Optional.ofNullable(responseMap.get("frate_steps")) + .flatMap(value -> InfoParser.parseInt(value)).orElse(1); for (AirbaseFeature f : AirbaseFeature.values()) { if ("1".equals(responseMap.get(f.getValue()))) { info.features.add(f); diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/discovery/DaikinACUnitDiscoveryService.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/discovery/DaikinACUnitDiscoveryService.java index a7dc76f360cf3..a3b84434ded50 100644 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/discovery/DaikinACUnitDiscoveryService.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/discovery/DaikinACUnitDiscoveryService.java @@ -39,11 +39,7 @@ import org.openhab.binding.daikin.internal.DaikinCommunicationForbiddenException; import org.openhab.binding.daikin.internal.DaikinHttpClientFactory; import org.openhab.binding.daikin.internal.DaikinWebTargets; -import org.openhab.binding.daikin.internal.api.BasicInfo; -import org.openhab.binding.daikin.internal.api.ControlInfo; import org.openhab.binding.daikin.internal.api.InfoParser; -import org.openhab.binding.daikin.internal.api.airbase.AirbaseBasicInfo; -import org.openhab.binding.daikin.internal.api.airbase.AirbaseControlInfo; import org.openhab.binding.daikin.internal.config.DaikinConfiguration; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinAcUnitHandler.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinAcUnitHandler.java index 6687bb5c4d114..6cfbb8598015a 100755 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinAcUnitHandler.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinAcUnitHandler.java @@ -13,28 +13,34 @@ package org.openhab.binding.daikin.internal.handler; import java.io.IOException; -import java.lang.IllegalArgumentException; +import java.math.BigDecimal; import java.util.Optional; +import java.util.stream.IntStream; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.jetty.client.HttpClient; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.daikin.internal.DaikinBindingConstants; import org.openhab.binding.daikin.internal.DaikinCommunicationException; import org.openhab.binding.daikin.internal.DaikinDynamicStateDescriptionProvider; +import org.openhab.binding.daikin.internal.DaikinWebTargets; import org.openhab.binding.daikin.internal.api.ControlInfo; +import org.openhab.binding.daikin.internal.api.EnergyInfoYear; import org.openhab.binding.daikin.internal.api.Enums.FanMovement; import org.openhab.binding.daikin.internal.api.Enums.FanSpeed; import org.openhab.binding.daikin.internal.api.Enums.HomekitMode; import org.openhab.binding.daikin.internal.api.Enums.Mode; +import org.openhab.binding.daikin.internal.api.Enums.SpecialModeKind; import org.openhab.binding.daikin.internal.api.SensorInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,50 +50,84 @@ * * @author Tim Waterhouse - Initial Contribution * @author Paul Smedley - Modifications to support Airbase Controllers - * + * @author Lukas Agethen - Added support for Energy Year reading, compressor frequency and powerful mode */ @NonNullByDefault public class DaikinAcUnitHandler extends DaikinBaseHandler { private final Logger logger = LoggerFactory.getLogger(DaikinAcUnitHandler.class); - public DaikinAcUnitHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider, @Nullable HttpClient httpClient) { + public DaikinAcUnitHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider, + @Nullable HttpClient httpClient) { super(thing, stateDescriptionProvider, httpClient); } @Override protected void pollStatus() throws IOException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } ControlInfo controlInfo = webTargets.getControlInfo(); updateStatus(ThingStatus.ONLINE); - if (controlInfo != null) { - updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF); - updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp); - - updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name())); - updateState(DaikinBindingConstants.CHANNEL_AC_FAN_SPEED, new StringType(controlInfo.fanSpeed.name())); - updateState(DaikinBindingConstants.CHANNEL_AC_FAN_DIR, new StringType(controlInfo.fanMovement.name())); - - if (!controlInfo.power) { - updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue())); - } else if (controlInfo.mode == Mode.COLD) { - updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue())); - } else if (controlInfo.mode == Mode.HEAT) { - updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue())); - } else if (controlInfo.mode == Mode.AUTO) { - updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue())); - } + updateState(DaikinBindingConstants.CHANNEL_AC_POWER, controlInfo.power ? OnOffType.ON : OnOffType.OFF); + updateTemperatureChannel(DaikinBindingConstants.CHANNEL_AC_TEMP, controlInfo.temp); + + updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(controlInfo.mode.name())); + updateState(DaikinBindingConstants.CHANNEL_AC_FAN_SPEED, new StringType(controlInfo.fanSpeed.name())); + updateState(DaikinBindingConstants.CHANNEL_AC_FAN_DIR, new StringType(controlInfo.fanMovement.name())); + + if (!controlInfo.power) { + updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.OFF.getValue())); + } else if (controlInfo.mode == Mode.COLD) { + updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.COOL.getValue())); + } else if (controlInfo.mode == Mode.HEAT) { + updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.HEAT.getValue())); + } else if (controlInfo.mode == Mode.AUTO) { + updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, new StringType(HomekitMode.AUTO.getValue())); + } + + updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE, new StringType(controlInfo.specialMode.name())); + + if (controlInfo.specialMode.isUndefined()) { + updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL, UnDefType.UNDEF); + } else { + updateState(DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL, + controlInfo.specialMode.isPowerfulActive() ? OnOffType.ON : OnOffType.OFF); } SensorInfo sensorInfo = webTargets.getSensorInfo(); - if (sensorInfo != null) { - updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp); + updateTemperatureChannel(DaikinBindingConstants.CHANNEL_INDOOR_TEMP, sensorInfo.indoortemp); + + updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp); + + if (sensorInfo.indoorhumidity.isPresent()) { + updateState(DaikinBindingConstants.CHANNEL_HUMIDITY, + new QuantityType<>(sensorInfo.indoorhumidity.get(), SmartHomeUnits.PERCENT)); + } else { + updateState(DaikinBindingConstants.CHANNEL_HUMIDITY, UnDefType.UNDEF); + } + + if (sensorInfo.compressorfrequency.isPresent()) { + updateState(DaikinBindingConstants.CHANNEL_CMP_FREQ, + new QuantityType<>(sensorInfo.compressorfrequency.get(), SmartHomeUnits.PERCENT)); + } else { + updateState(DaikinBindingConstants.CHANNEL_CMP_FREQ, UnDefType.UNDEF); + } - updateTemperatureChannel(DaikinBindingConstants.CHANNEL_OUTDOOR_TEMP, sensorInfo.outdoortemp); + try { + EnergyInfoYear energyInfoYear = webTargets.getEnergyInfoYear(); - if (sensorInfo.indoorhumidity.isPresent()) { - updateState(DaikinBindingConstants.CHANNEL_HUMIDITY, new DecimalType(sensorInfo.indoorhumidity.get())); - } else { - updateState(DaikinBindingConstants.CHANNEL_HUMIDITY, UnDefType.UNDEF); + if (energyInfoYear.energyHeatingThisYear.isPresent()) { + updateEnergyYearChannel(DaikinBindingConstants.CHANNEL_ENERGY_HEATING_CURRENTYEAR_PREFIX, + energyInfoYear.energyHeatingThisYear); + } + if (energyInfoYear.energyCoolingThisYear.isPresent()) { + updateEnergyYearChannel(DaikinBindingConstants.CHANNEL_ENERGY_COOLING_CURRENTYEAR_PREFIX, + energyInfoYear.energyCoolingThisYear); } + } catch (DaikinCommunicationException e) { + // Suppress any error if energy info is not supported. + logger.debug("getEnergyInfoYear() error: {}", e.getMessage()); } } @@ -101,12 +141,22 @@ protected boolean handleCommandInternal(ChannelUID channelUID, Command command) return true; } break; + case DaikinBindingConstants.CHANNEL_AC_SPECIALMODE_POWERFUL: + if (command instanceof OnOffType) { + changeSpecialModePowerful(((OnOffType) command).equals(OnOffType.ON)); + return true; + } + break; } return false; } @Override protected void changePower(boolean power) throws DaikinCommunicationException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } ControlInfo info = webTargets.getControlInfo(); info.power = power; webTargets.setControlInfo(info); @@ -114,6 +164,10 @@ protected void changePower(boolean power) throws DaikinCommunicationException { @Override protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } ControlInfo info = webTargets.getControlInfo(); info.temp = Optional.of(newTemperature); webTargets.setControlInfo(info); @@ -121,6 +175,10 @@ protected void changeSetPoint(double newTemperature) throws DaikinCommunicationE @Override protected void changeMode(String mode) throws DaikinCommunicationException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } Mode newMode; try { newMode = Mode.valueOf(mode); @@ -135,6 +193,10 @@ protected void changeMode(String mode) throws DaikinCommunicationException { @Override protected void changeFanSpeed(String fanSpeed) throws DaikinCommunicationException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } FanSpeed newSpeed; try { newSpeed = FanSpeed.valueOf(fanSpeed); @@ -148,6 +210,10 @@ protected void changeFanSpeed(String fanSpeed) throws DaikinCommunicationExcepti } protected void changeFanDir(String fanDir) throws DaikinCommunicationException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } FanMovement newMovement; try { newMovement = FanMovement.valueOf(fanDir); @@ -160,12 +226,47 @@ protected void changeFanDir(String fanDir) throws DaikinCommunicationException { webTargets.setControlInfo(info); } + /** + * + * @param powerfulMode + * @return Is change successful + * @throws DaikinCommunicationException + */ + protected boolean changeSpecialModePowerful(boolean powerfulMode) throws DaikinCommunicationException { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return false; + } + return webTargets.setSpecialMode(SpecialModeKind.POWERFUL, powerfulMode); + } + + /** + * Updates energy year channels. Values are provided in hundreds of Watt + * + * @param channelPrefix + * @param maybePower + */ + protected void updateEnergyYearChannel(String channelPrefix, Optional maybePower) { + IntStream.range(1, 13) + .forEach(i -> updateState( + String.format(DaikinBindingConstants.CHANNEL_ENERGY_STRING_FORMAT, channelPrefix, i), + maybePower.map(t -> new QuantityType<>(BigDecimal.valueOf(t[i - 1].longValue(), 1), + SmartHomeUnits.KILOWATT_HOUR)).orElse(UnDefType.UNDEF)) + + ); + + } + @Override protected void registerUuid(@Nullable String key) { if (key == null) { return; } try { + DaikinWebTargets webTargets = this.webTargets; + if (webTargets == null) { + return; + } webTargets.registerUuid(key); } catch (Exception e) { // suppress exceptions diff --git a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinBaseHandler.java b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinBaseHandler.java index c0b901a7144bc..0eb956a0aeb56 100755 --- a/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinBaseHandler.java +++ b/bundles/org.openhab.binding.daikin/src/main/java/org/openhab/binding/daikin/internal/handler/DaikinBaseHandler.java @@ -19,9 +19,9 @@ import javax.measure.quantity.Temperature; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.QuantityType; @@ -85,7 +85,8 @@ protected abstract boolean handleCommandInternal(ChannelUID channelUID, Command protected abstract void registerUuid(@Nullable String key); - public DaikinBaseHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider, @Nullable HttpClient httpClient) { + public DaikinBaseHandler(Thing thing, DaikinDynamicStateDescriptionProvider stateDescriptionProvider, + @Nullable HttpClient httpClient) { super(thing); this.stateDescriptionProvider = stateDescriptionProvider; this.httpClient = httpClient; @@ -190,7 +191,8 @@ private synchronized void poll() { registerUuid(config.key); uuidRegistrationAttempted = true; } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR , "Access denied. Check uuid/key."); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Access denied. Check uuid/key."); logger.warn("{} access denied by adapter. Check uuid/key.", thing.getUID()); } } catch (IOException e) { @@ -201,8 +203,8 @@ private synchronized void poll() { } protected void updateTemperatureChannel(String channel, Optional maybeTemperature) { - updateState(channel, maybeTemperature. map(t -> new QuantityType<>(new DecimalType(t), SIUnits.CELSIUS)) - .orElse(UnDefType.UNDEF)); + updateState(channel, + maybeTemperature.map(t -> new QuantityType<>(t, SIUnits.CELSIUS)).orElse(UnDefType.UNDEF)); } /** @@ -239,4 +241,5 @@ private void changeHomekitMode(String homekitmode) throws DaikinCommunicationExc } } } + } diff --git a/bundles/org.openhab.binding.daikin/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.daikin/src/main/resources/ESH-INF/thing/thing-types.xml index 7a4059435a764..75e543250f98b 100644 --- a/bundles/org.openhab.binding.daikin/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.daikin/src/main/resources/ESH-INF/thing/thing-types.xml @@ -18,6 +18,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + host @@ -44,11 +71,10 @@ - host - + @@ -82,10 +108,10 @@ - Number + Number:Dimensionless The indoor humidity as measured by the A/C unit - + @@ -147,6 +173,172 @@ + + + Number:Dimensionless + + Current compressor frequency + + + + String + + Current special mode + + + + Switch + + Powerful mode switch + + + Number:Energy + + The energy usage for heating this year January + + + + Number:Energy + + The energy usage for heating this year February + + + + + Number:Energy + + The energy usage for heating this year March + + + + + Number:Energy + + The energy usage for heating this year April + + + + Number:Energy + + The energy usage for heating this year May + + + + Number:Energy + + The energy usage for heating this year June + + + + Number:Energy + + The energy usage for heating this year July + + + + Number:Energy + + The energy usage for heating this year August + + + + Number:Energy + + The energy usage for heating this year September + + + + Number:Energy + + The energy usage for heating this year October + + + + Number:Energy + + The energy usage for heating this year November + + + + Number:Energy + + The energy usage for heating this year December + + + + Number:Energy + + The energy usage for cooling this year January + + + + Number:Energy + + The energy usage for cooling this year February + + + + + Number:Energy + + The energy usage for cooling this year March + + + + + Number:Energy + + The energy usage for cooling this year April + + + + Number:Energy + + The energy usage for cooling this year May + + + + Number:Energy + + The energy usage for cooling this year June + + + + Number:Energy + + The energy usage for cooling this year July + + + + Number:Energy + + The energy usage for cooling this year August + + + + Number:Energy + + The energy usage for cooling this year September + + + + Number:Energy + + The energy usage for cooling this year October + + + + Number:Energy + + The energy usage for cooling this year November + + + + Number:Energy + + The energy usage for cooling this year December + + String