[daikin] Update channels immediately after a successful API command (#17149)

* [daikin] Update channels immediately after a successful API command

Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
jimtng 2024-08-28 19:43:16 +10:00 committed by GitHub
parent 8725dcbe28
commit 198b9b184d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 185 additions and 106 deletions

View File

@ -15,7 +15,6 @@ package org.openhab.binding.daikin.internal;
import java.io.EOFException; import java.io.EOFException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -33,7 +32,6 @@ import org.openhab.binding.daikin.internal.api.DemandControl;
import org.openhab.binding.daikin.internal.api.EnergyInfoDayAndWeek; import org.openhab.binding.daikin.internal.api.EnergyInfoDayAndWeek;
import org.openhab.binding.daikin.internal.api.EnergyInfoYear; import org.openhab.binding.daikin.internal.api.EnergyInfoYear;
import org.openhab.binding.daikin.internal.api.Enums.SpecialMode; import org.openhab.binding.daikin.internal.api.Enums.SpecialMode;
import org.openhab.binding.daikin.internal.api.InfoParser;
import org.openhab.binding.daikin.internal.api.SensorInfo; import org.openhab.binding.daikin.internal.api.SensorInfo;
import org.openhab.binding.daikin.internal.api.airbase.AirbaseBasicInfo; 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.AirbaseControlInfo;
@ -119,9 +117,7 @@ public class DaikinWebTargets {
public boolean setControlInfo(ControlInfo info) throws DaikinCommunicationException { public boolean setControlInfo(ControlInfo info) throws DaikinCommunicationException {
Map<String, String> queryParams = info.getParamString(); Map<String, String> queryParams = info.getParamString();
String result = invoke(setControlInfoUri, queryParams); return isSuccessful(invoke(setControlInfoUri, queryParams));
Map<String, String> responseMap = InfoParser.parse(result);
return Optional.ofNullable(responseMap.get("ret")).orElse("").equals("OK");
} }
public SensorInfo getSensorInfo() throws DaikinCommunicationException { public SensorInfo getSensorInfo() throws DaikinCommunicationException {
@ -146,7 +142,7 @@ public class DaikinWebTargets {
return EnergyInfoDayAndWeek.parse(response); return EnergyInfoDayAndWeek.parse(response);
} }
public void setSpecialMode(SpecialMode newMode) throws DaikinCommunicationException { public boolean setSpecialMode(SpecialMode newMode) throws DaikinCommunicationException {
Map<String, String> queryParams = new HashMap<>(); Map<String, String> queryParams = new HashMap<>();
if (newMode == SpecialMode.NORMAL) { if (newMode == SpecialMode.NORMAL) {
queryParams.put("set_spmode", "0"); queryParams.put("set_spmode", "0");
@ -160,17 +156,23 @@ public class DaikinWebTargets {
queryParams.put("spmode_kind", newMode.getValue()); queryParams.put("spmode_kind", newMode.getValue());
} }
String response = invoke(setSpecialModeUri, queryParams); String response = invoke(setSpecialModeUri, queryParams);
if (!response.contains("ret=OK")) { if (isSuccessful(response)) {
return true;
} else {
logger.warn("Error setting special mode. Response: '{}'", response); logger.warn("Error setting special mode. Response: '{}'", response);
return false;
} }
} }
public void setStreamerMode(boolean state) throws DaikinCommunicationException { public boolean setStreamerMode(boolean state) throws DaikinCommunicationException {
Map<String, String> queryParams = new HashMap<>(); Map<String, String> queryParams = new HashMap<>();
queryParams.put("en_streamer", state ? "1" : "0"); queryParams.put("en_streamer", state ? "1" : "0");
String response = invoke(setSpecialModeUri, queryParams); String response = invoke(setSpecialModeUri, queryParams);
if (!response.contains("ret=OK")) { if (isSuccessful(response)) {
return true;
} else {
logger.warn("Error setting streamer mode. Response: '{}'", response); logger.warn("Error setting streamer mode. Response: '{}'", response);
return false;
} }
} }
@ -181,9 +183,7 @@ public class DaikinWebTargets {
public boolean setDemandControl(DemandControl info) throws DaikinCommunicationException { public boolean setDemandControl(DemandControl info) throws DaikinCommunicationException {
Map<String, String> queryParams = info.getParamString(); Map<String, String> queryParams = info.getParamString();
String result = invoke(setDemandControlUri, queryParams); return isSuccessful(invoke(setDemandControlUri, queryParams));
Map<String, String> responseMap = InfoParser.parse(result);
return Optional.ofNullable(responseMap.get("ret")).orElse("").equals("OK");
} }
// Daikin Airbase API // Daikin Airbase API
@ -192,9 +192,9 @@ public class DaikinWebTargets {
return AirbaseControlInfo.parse(response); return AirbaseControlInfo.parse(response);
} }
public void setAirbaseControlInfo(AirbaseControlInfo info) throws DaikinCommunicationException { public boolean setAirbaseControlInfo(AirbaseControlInfo info) throws DaikinCommunicationException {
Map<String, String> queryParams = info.getParamString(); Map<String, String> queryParams = info.getParamString();
invoke(setAirbaseControlInfoUri, queryParams); return isSuccessful(invoke(setAirbaseControlInfoUri, queryParams));
} }
public SensorInfo getAirbaseSensorInfo() throws DaikinCommunicationException { public SensorInfo getAirbaseSensorInfo() throws DaikinCommunicationException {
@ -217,9 +217,13 @@ public class DaikinWebTargets {
return AirbaseZoneInfo.parse(response); return AirbaseZoneInfo.parse(response);
} }
public void setAirbaseZoneInfo(AirbaseZoneInfo zoneinfo) throws DaikinCommunicationException { public boolean setAirbaseZoneInfo(AirbaseZoneInfo zoneinfo) throws DaikinCommunicationException {
Map<String, String> queryParams = zoneinfo.getParamString(); Map<String, String> queryParams = zoneinfo.getParamString();
invoke(setAirbaseZoneInfoUri, queryParams); return isSuccessful(invoke(setAirbaseZoneInfoUri, queryParams));
}
private boolean isSuccessful(String response) {
return response.contains("ret=OK");
} }
private String invoke(String uri) throws DaikinCommunicationException { private String invoke(String uri) throws DaikinCommunicationException {

View File

@ -129,7 +129,6 @@ public class Enums {
HEAT("heat"), HEAT("heat"),
OFF("off"); OFF("off");
private static final Logger LOGGER = LoggerFactory.getLogger(HomekitMode.class);
private final String value; private final String value;
HomekitMode(String value) { HomekitMode(String value) {

View File

@ -192,19 +192,25 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
switch (channelUID.getId()) { switch (channelUID.getId()) {
case DaikinBindingConstants.CHANNEL_AC_FAN_DIR: case DaikinBindingConstants.CHANNEL_AC_FAN_DIR:
if (command instanceof StringType stringCommand) { if (command instanceof StringType stringCommand) {
changeFanDir(stringCommand.toString()); if (changeFanDir(stringCommand.toString())) {
updateState(channelUID, stringCommand);
}
return true; return true;
} }
break; break;
case DaikinBindingConstants.CHANNEL_AC_SPECIALMODE: case DaikinBindingConstants.CHANNEL_AC_SPECIALMODE:
if (command instanceof StringType stringCommand) { if (command instanceof StringType stringCommand) {
changeSpecialMode(stringCommand.toString()); if (changeSpecialMode(stringCommand.toString())) {
updateState(channelUID, stringCommand);
}
return true; return true;
} }
break; break;
case DaikinBindingConstants.CHANNEL_AC_STREAMER: case DaikinBindingConstants.CHANNEL_AC_STREAMER:
if (command instanceof OnOffType onOffCommand) { if (command instanceof OnOffType onOffCommand) {
changeStreamer(onOffCommand.equals(OnOffType.ON)); if (changeStreamer(onOffCommand.equals(OnOffType.ON))) {
updateState(channelUID, onOffCommand);
}
return true; return true;
} }
break; break;
@ -216,13 +222,21 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
break; break;
case DaikinBindingConstants.CHANNEL_AC_DEMAND_MAX_POWER: case DaikinBindingConstants.CHANNEL_AC_DEMAND_MAX_POWER:
if (command instanceof PercentType percentCommand) { if (command instanceof PercentType percentCommand) {
changeDemandMaxPower(percentCommand.intValue()); if (changeDemandMaxPower(percentCommand.intValue())) {
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_MODE,
new StringType(DemandControlMode.MANUAL.name()));
updateState(channelUID, percentCommand);
}
return true; return true;
} }
break; break;
case DaikinBindingConstants.CHANNEL_AC_DEMAND_SCHEDULE: case DaikinBindingConstants.CHANNEL_AC_DEMAND_SCHEDULE:
if (command instanceof StringType stringCommand) { if (command instanceof StringType stringCommand) {
changeDemandSchedule(stringCommand.toString()); if (changeDemandSchedule(stringCommand.toString())) {
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_MODE,
new StringType(DemandControlMode.SCHEDULED.name()));
updateState(channelUID, stringCommand);
}
return true; return true;
} }
break; break;
@ -231,27 +245,27 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
} }
@Override @Override
protected void changePower(boolean power) throws DaikinCommunicationException { protected boolean changePower(boolean power) throws DaikinCommunicationException {
ControlInfo info = webTargets.getControlInfo(); ControlInfo info = webTargets.getControlInfo();
info.power = power; info.power = power;
webTargets.setControlInfo(info); return webTargets.setControlInfo(info);
} }
@Override @Override
protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException { protected boolean changeSetPoint(double newTemperature) throws DaikinCommunicationException {
ControlInfo info = webTargets.getControlInfo(); ControlInfo info = webTargets.getControlInfo();
info.temp = Optional.of(newTemperature); info.temp = Optional.of(newTemperature);
webTargets.setControlInfo(info); return webTargets.setControlInfo(info);
} }
@Override @Override
protected void changeMode(String mode) throws DaikinCommunicationException { protected boolean changeMode(String mode) throws DaikinCommunicationException {
Mode newMode; Mode newMode;
try { try {
newMode = Mode.valueOf(mode); newMode = Mode.valueOf(mode);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
logger.warn("Invalid mode: {}. Valid values: {}", mode, Mode.values()); logger.warn("Invalid mode: {}. Valid values: {}", mode, Mode.values());
return; return false;
} }
ControlInfo info = webTargets.getControlInfo(); ControlInfo info = webTargets.getControlInfo();
info.mode = newMode; info.mode = newMode;
@ -263,55 +277,58 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
// If mode=0 is not accepted try AUTO1 (mode=1) // If mode=0 is not accepted try AUTO1 (mode=1)
if (!accepted && newMode == Mode.AUTO && autoModeValue.isEmpty()) { if (!accepted && newMode == Mode.AUTO && autoModeValue.isEmpty()) {
info.autoModeValue = Mode.AUTO1.getValue(); info.autoModeValue = Mode.AUTO1.getValue();
if (webTargets.setControlInfo(info)) { accepted = webTargets.setControlInfo(info);
if (accepted) {
autoModeValue = Optional.of(info.autoModeValue); autoModeValue = Optional.of(info.autoModeValue);
logger.debug("AUTO uses mode={}", info.autoModeValue); logger.debug("AUTO uses mode={}", info.autoModeValue);
} else { } else {
logger.warn("AUTO mode not accepted with mode=0 or mode=1"); logger.warn("AUTO mode not accepted with mode=0 or mode=1");
} }
} }
return accepted;
} }
@Override @Override
protected void changeFanSpeed(String fanSpeed) throws DaikinCommunicationException { protected boolean changeFanSpeed(String fanSpeed) throws DaikinCommunicationException {
FanSpeed newSpeed; FanSpeed newSpeed;
try { try {
newSpeed = FanSpeed.valueOf(fanSpeed); newSpeed = FanSpeed.valueOf(fanSpeed);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
logger.warn("Invalid fan speed: {}. Valid values: {}", fanSpeed, FanSpeed.values()); logger.warn("Invalid fan speed: {}. Valid values: {}", fanSpeed, FanSpeed.values());
return; return false;
} }
ControlInfo info = webTargets.getControlInfo(); ControlInfo info = webTargets.getControlInfo();
info.fanSpeed = newSpeed; info.fanSpeed = newSpeed;
webTargets.setControlInfo(info); return webTargets.setControlInfo(info);
} }
protected void changeFanDir(String fanDir) throws DaikinCommunicationException { protected boolean changeFanDir(String fanDir) throws DaikinCommunicationException {
FanMovement newMovement; FanMovement newMovement;
try { try {
newMovement = FanMovement.valueOf(fanDir); newMovement = FanMovement.valueOf(fanDir);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
logger.warn("Invalid fan direction: {}. Valid values: {}", fanDir, FanMovement.values()); logger.warn("Invalid fan direction: {}. Valid values: {}", fanDir, FanMovement.values());
return; return false;
} }
ControlInfo info = webTargets.getControlInfo(); ControlInfo info = webTargets.getControlInfo();
info.fanMovement = newMovement; info.fanMovement = newMovement;
webTargets.setControlInfo(info); return webTargets.setControlInfo(info);
} }
protected void changeSpecialMode(String specialMode) throws DaikinCommunicationException { protected boolean changeSpecialMode(String specialMode) throws DaikinCommunicationException {
SpecialMode newMode; SpecialMode newMode;
try { try {
newMode = SpecialMode.valueOf(specialMode); newMode = SpecialMode.valueOf(specialMode);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.warn("Invalid specialmode: {}. Valid values: {}", specialMode, SpecialMode.values()); logger.warn("Invalid specialmode: {}. Valid values: {}", specialMode, SpecialMode.values());
return; return false;
} }
webTargets.setSpecialMode(newMode); return webTargets.setSpecialMode(newMode);
} }
protected void changeStreamer(boolean streamerMode) throws DaikinCommunicationException { protected boolean changeStreamer(boolean streamerMode) throws DaikinCommunicationException {
webTargets.setStreamerMode(streamerMode); return webTargets.setStreamerMode(streamerMode);
} }
protected void changeDemandMode(String mode) throws DaikinCommunicationException { protected void changeDemandMode(String mode) throws DaikinCommunicationException {
@ -323,40 +340,54 @@ public class DaikinAcUnitHandler extends DaikinBaseHandler {
return; return;
} }
DemandControl demandInfo = webTargets.getDemandControl(); DemandControl demandInfo = webTargets.getDemandControl();
boolean scheduleChanged = false;
boolean maxPowerChanged = false;
if (demandInfo.mode != newMode) { if (demandInfo.mode != newMode) {
if (newMode == DemandControlMode.SCHEDULED && savedDemandControlSchedule.isPresent()) { if (newMode == DemandControlMode.SCHEDULED && savedDemandControlSchedule.isPresent()) {
// restore previously saved schedule // restore previously saved schedule
demandInfo.setSchedule(savedDemandControlSchedule.get()); demandInfo.setSchedule(savedDemandControlSchedule.get());
scheduleChanged = true;
} }
if (newMode == DemandControlMode.MANUAL && savedDemandControlMaxPower.isPresent()) { if (newMode == DemandControlMode.MANUAL && savedDemandControlMaxPower.isPresent()) {
// restore previously saved maxPower // restore previously saved maxPower
demandInfo.maxPower = savedDemandControlMaxPower.get(); demandInfo.maxPower = savedDemandControlMaxPower.get();
maxPowerChanged = true;
} }
} }
demandInfo.mode = newMode; demandInfo.mode = newMode;
webTargets.setDemandControl(demandInfo); if (webTargets.setDemandControl(demandInfo)) {
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_MODE, new StringType(newMode.name()));
if (scheduleChanged) {
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_SCHEDULE,
new StringType(savedDemandControlSchedule.get()));
}
if (maxPowerChanged) {
updateState(DaikinBindingConstants.CHANNEL_AC_DEMAND_MAX_POWER,
new PercentType(savedDemandControlMaxPower.get()));
}
}
} }
protected void changeDemandMaxPower(int maxPower) throws DaikinCommunicationException { protected boolean changeDemandMaxPower(int maxPower) throws DaikinCommunicationException {
DemandControl demandInfo = webTargets.getDemandControl(); DemandControl demandInfo = webTargets.getDemandControl();
demandInfo.mode = DemandControlMode.MANUAL; demandInfo.mode = DemandControlMode.MANUAL;
demandInfo.maxPower = maxPower; demandInfo.maxPower = maxPower;
webTargets.setDemandControl(demandInfo);
savedDemandControlMaxPower = Optional.of(maxPower); savedDemandControlMaxPower = Optional.of(maxPower);
return webTargets.setDemandControl(demandInfo);
} }
protected void changeDemandSchedule(String schedule) throws DaikinCommunicationException { protected boolean changeDemandSchedule(String schedule) throws DaikinCommunicationException {
DemandControl demandInfo = webTargets.getDemandControl(); DemandControl demandInfo = webTargets.getDemandControl();
try { try {
demandInfo.setSchedule(schedule); demandInfo.setSchedule(schedule);
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.warn("Invalid schedule: {}. {}", schedule, e.getMessage()); logger.warn("Invalid schedule: {}. {}", schedule, e.getMessage());
return; return false;
} }
demandInfo.mode = DemandControlMode.SCHEDULED; demandInfo.mode = DemandControlMode.SCHEDULED;
webTargets.setDemandControl(demandInfo);
savedDemandControlSchedule = Optional.of(demandInfo.getSchedule()); savedDemandControlSchedule = Optional.of(demandInfo.getSchedule());
return webTargets.setDemandControl(demandInfo);
} }
/** /**

View File

@ -64,8 +64,10 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
throws DaikinCommunicationException { throws DaikinCommunicationException {
if (channelUID.getId().startsWith(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE)) { if (channelUID.getId().startsWith(DaikinBindingConstants.CHANNEL_AIRBASE_AC_ZONE)) {
int zoneNumber = Integer.parseInt(channelUID.getId().substring(4)); int zoneNumber = Integer.parseInt(channelUID.getId().substring(4));
if (command instanceof OnOffType) { if (command instanceof OnOffType onOffCommand) {
changeZone(zoneNumber, command == OnOffType.ON); if (changeZone(zoneNumber, onOffCommand == OnOffType.ON)) {
updateState(channelUID, onOffCommand);
}
return true; return true;
} }
} }
@ -110,63 +112,63 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
} }
@Override @Override
protected void changePower(boolean power) throws DaikinCommunicationException { protected boolean changePower(boolean power) throws DaikinCommunicationException {
AirbaseControlInfo info = webTargets.getAirbaseControlInfo(); AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
info.power = power; info.power = power;
webTargets.setAirbaseControlInfo(info); return webTargets.setAirbaseControlInfo(info);
} }
@Override @Override
protected void changeSetPoint(double newTemperature) throws DaikinCommunicationException { protected boolean changeSetPoint(double newTemperature) throws DaikinCommunicationException {
AirbaseControlInfo info = webTargets.getAirbaseControlInfo(); AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
info.temp = Optional.of(newTemperature); info.temp = Optional.of(newTemperature);
webTargets.setAirbaseControlInfo(info); return webTargets.setAirbaseControlInfo(info);
} }
@Override @Override
protected void changeMode(String mode) throws DaikinCommunicationException { protected boolean changeMode(String mode) throws DaikinCommunicationException {
AirbaseMode newMode; AirbaseMode newMode;
try { try {
newMode = AirbaseMode.valueOf(mode); newMode = AirbaseMode.valueOf(mode);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
logger.warn("Invalid mode: {}. Valid values: {}", mode, AirbaseMode.values()); logger.warn("Invalid mode: {}. Valid values: {}", mode, AirbaseMode.values());
return; return false;
} }
if (airbaseModelInfo != null) { if (airbaseModelInfo != null) {
if ((newMode == AirbaseMode.AUTO && !airbaseModelInfo.features.contains(AirbaseFeature.AUTO)) if ((newMode == AirbaseMode.AUTO && !airbaseModelInfo.features.contains(AirbaseFeature.AUTO))
|| (newMode == AirbaseMode.DRY && !airbaseModelInfo.features.contains(AirbaseFeature.DRY))) { || (newMode == AirbaseMode.DRY && !airbaseModelInfo.features.contains(AirbaseFeature.DRY))) {
logger.warn("{} mode is not supported by your controller", mode); logger.warn("{} mode is not supported by your controller", mode);
return; return false;
} }
} }
AirbaseControlInfo info = webTargets.getAirbaseControlInfo(); AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
info.mode = newMode; info.mode = newMode;
webTargets.setAirbaseControlInfo(info); return webTargets.setAirbaseControlInfo(info);
} }
@Override @Override
protected void changeFanSpeed(String speed) throws DaikinCommunicationException { protected boolean changeFanSpeed(String speed) throws DaikinCommunicationException {
AirbaseFanSpeed newFanSpeed; AirbaseFanSpeed newFanSpeed;
try { try {
newFanSpeed = AirbaseFanSpeed.valueOf(speed); newFanSpeed = AirbaseFanSpeed.valueOf(speed);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
logger.warn("Invalid fan speed: {}. Valid values: {}", speed, AirbaseFanSpeed.values()); logger.warn("Invalid fan speed: {}. Valid values: {}", speed, AirbaseFanSpeed.values());
return; return false;
} }
if (airbaseModelInfo != null) { if (airbaseModelInfo != null) {
if (EnumSet.range(AirbaseFanSpeed.AUTO_LEVEL_1, AirbaseFanSpeed.AUTO_LEVEL_5).contains(newFanSpeed) if (EnumSet.range(AirbaseFanSpeed.AUTO_LEVEL_1, AirbaseFanSpeed.AUTO_LEVEL_5).contains(newFanSpeed)
&& !airbaseModelInfo.features.contains(AirbaseFeature.FRATE_AUTO)) { && !airbaseModelInfo.features.contains(AirbaseFeature.FRATE_AUTO)) {
logger.warn("Fan AUTO_LEVEL_X is not supported by your controller"); logger.warn("Fan AUTO_LEVEL_X is not supported by your controller");
return; return false;
} }
if (newFanSpeed == AirbaseFanSpeed.AIRSIDE && !airbaseModelInfo.features.contains(AirbaseFeature.AIRSIDE)) { if (newFanSpeed == AirbaseFanSpeed.AIRSIDE && !airbaseModelInfo.features.contains(AirbaseFeature.AIRSIDE)) {
logger.warn("Airside is not supported by your controller"); logger.warn("Airside is not supported by your controller");
return; return false;
} }
} }
AirbaseControlInfo info = webTargets.getAirbaseControlInfo(); AirbaseControlInfo info = webTargets.getAirbaseControlInfo();
info.fanSpeed = newFanSpeed; info.fanSpeed = newFanSpeed;
webTargets.setAirbaseControlInfo(info); return webTargets.setAirbaseControlInfo(info);
} }
/** /**
@ -178,7 +180,7 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
* @param command true to turn on the zone, false to turn it off * @param command true to turn on the zone, false to turn it off
* *
*/ */
protected void changeZone(int zone, boolean command) throws DaikinCommunicationException { protected boolean changeZone(int zone, boolean command) throws DaikinCommunicationException {
AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo(); AirbaseZoneInfo zoneInfo = webTargets.getAirbaseZoneInfo();
long maxZones = zoneInfo.zone.length; long maxZones = zoneInfo.zone.length;
@ -188,11 +190,11 @@ public class DaikinAirbaseUnitHandler extends DaikinBaseHandler {
if (zone <= 0 || zone > maxZones) { if (zone <= 0 || zone > maxZones) {
logger.warn("The given zone number ({}) is outside the number of zones supported by the controller ({})", logger.warn("The given zone number ({}) is outside the number of zones supported by the controller ({})",
zone, maxZones); zone, maxZones);
return; return false;
} }
zoneInfo.zone[zone - 1] = command; zoneInfo.zone[zone - 1] = command;
webTargets.setAirbaseZoneInfo(zoneInfo); return webTargets.setAirbaseZoneInfo(zoneInfo);
} }
@Override @Override

View File

@ -70,13 +70,13 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
// Abstract methods to be overridden by specific Daikin implementation class // Abstract methods to be overridden by specific Daikin implementation class
protected abstract void pollStatus() throws DaikinCommunicationException; protected abstract void pollStatus() throws DaikinCommunicationException;
protected abstract void changePower(boolean power) throws DaikinCommunicationException; protected abstract boolean changePower(boolean power) throws DaikinCommunicationException;
protected abstract void changeSetPoint(double newTemperature) throws DaikinCommunicationException; protected abstract boolean changeSetPoint(double newTemperature) throws DaikinCommunicationException;
protected abstract void changeMode(String mode) throws DaikinCommunicationException; protected abstract boolean changeMode(String mode) throws DaikinCommunicationException;
protected abstract void changeFanSpeed(String fanSpeed) throws DaikinCommunicationException; protected abstract boolean changeFanSpeed(String fanSpeed) throws DaikinCommunicationException;
// Power, Temp, Fan and Mode are handled in this base class. Override this to handle additional channels. // Power, Temp, Fan and Mode are handled in this base class. Override this to handle additional channels.
protected abstract boolean handleCommandInternal(ChannelUID channelUID, Command command) protected abstract boolean handleCommandInternal(ChannelUID channelUID, Command command)
@ -104,31 +104,54 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
switch (channelUID.getId()) { switch (channelUID.getId()) {
case DaikinBindingConstants.CHANNEL_AC_POWER: case DaikinBindingConstants.CHANNEL_AC_POWER:
if (command instanceof OnOffType onOffCommand) { if (command instanceof OnOffType onOffCommand) {
changePower(onOffCommand.equals(OnOffType.ON)); if (changePower(onOffCommand.equals(OnOffType.ON))) {
updateState(channelUID, onOffCommand);
}
return; return;
} }
break; break;
case DaikinBindingConstants.CHANNEL_AC_TEMP: case DaikinBindingConstants.CHANNEL_AC_TEMP:
if (changeSetPoint(command)) { double newTemperature;
return; State newState = UnDefType.UNDEF;
if (command instanceof DecimalType decimalCommand) {
newTemperature = decimalCommand.doubleValue();
newState = decimalCommand;
} else if (command instanceof QuantityType) {
QuantityType<Temperature> quantityCommand = (QuantityType<Temperature>) command;
newTemperature = quantityCommand.toUnit(SIUnits.CELSIUS).doubleValue();
newState = quantityCommand;
} else {
break; // Exit switch statement but proceed to log about unsupported command type
} }
break;
// Only half degree increments are allowed, all others are silently rejected by the A/C units
newTemperature = Math.round(newTemperature * 2) / 2.0;
if (changeSetPoint(newTemperature)) {
updateState(channelUID, newState);
}
return; // return here and don't log about wrong type below
case DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED: case DaikinBindingConstants.CHANNEL_AIRBASE_AC_FAN_SPEED:
case DaikinBindingConstants.CHANNEL_AC_FAN_SPEED: case DaikinBindingConstants.CHANNEL_AC_FAN_SPEED:
if (command instanceof StringType stringCommand) { if (command instanceof StringType stringCommand) {
changeFanSpeed(stringCommand.toString()); if (changeFanSpeed(stringCommand.toString())) {
updateState(channelUID, stringCommand);
}
return; return;
} }
break; break;
case DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE: case DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE:
if (command instanceof StringType) { if (command instanceof StringType stringCommand) {
changeHomekitMode(command.toString()); if (changeHomekitMode(stringCommand.toString())) {
updateState(DaikinBindingConstants.CHANNEL_AC_HOMEKITMODE, stringCommand);
}
return; return;
} }
break; break;
case DaikinBindingConstants.CHANNEL_AC_MODE: case DaikinBindingConstants.CHANNEL_AC_MODE:
if (command instanceof StringType stringCommand) { if (command instanceof StringType stringCommand) {
changeMode(stringCommand.toString()); if (changeMode(stringCommand.toString())) {
updateState(channelUID, stringCommand);
}
return; return;
} }
break; break;
@ -210,37 +233,37 @@ public abstract class DaikinBaseHandler extends BaseThingHandler {
maybeTemperature.<State> map(t -> new QuantityType<>(t, SIUnits.CELSIUS)).orElse(UnDefType.UNDEF))); maybeTemperature.<State> map(t -> new QuantityType<>(t, SIUnits.CELSIUS)).orElse(UnDefType.UNDEF)));
} }
/** private boolean changeHomekitMode(String homekitmode) throws DaikinCommunicationException {
* @return true if the command was of an expected type, false otherwise try {
*/ HomekitMode mode = HomekitMode.valueOf(homekitmode.toUpperCase());
private boolean changeSetPoint(Command command) throws DaikinCommunicationException { boolean power = mode != HomekitMode.OFF;
double newTemperature; if (!changePower(power)) {
if (command instanceof DecimalType decimalCommand) {
newTemperature = decimalCommand.doubleValue();
} else if (command instanceof QuantityType) {
newTemperature = ((QuantityType<Temperature>) command).toUnit(SIUnits.CELSIUS).doubleValue();
} else {
return false; return false;
} }
// Only half degree increments are allowed, all others are silently rejected by the A/C units boolean changeModeSuccess = false;
newTemperature = Math.round(newTemperature * 2) / 2.0; updateState(DaikinBindingConstants.CHANNEL_AC_POWER, OnOffType.from(power));
changeSetPoint(newTemperature);
String newMode = switch (mode) {
case AUTO -> "AUTO";
case HEAT -> "HEAT";
case COOL -> "COLD";
case OFF -> null;
};
if (newMode == null) {
return true; return true;
} }
private void changeHomekitMode(String homekitmode) throws DaikinCommunicationException { if (changeMode(newMode)) {
if (HomekitMode.OFF.getValue().equals(homekitmode)) { updateState(DaikinBindingConstants.CHANNEL_AC_MODE, new StringType(newMode));
changePower(false); return true;
} else { }
changePower(true);
if (HomekitMode.AUTO.getValue().equals(homekitmode)) { return false;
changeMode("AUTO"); } catch (IllegalArgumentException e) {
} else if (HomekitMode.HEAT.getValue().equals(homekitmode)) { logger.warn("Invalid homekit mode: {}", homekitmode);
changeMode("HEAT"); return false;
} else if (HomekitMode.COOL.getValue().equals(homekitmode)) {
changeMode("COLD");
}
} }
} }
} }

View File

@ -94,6 +94,7 @@
<label>Power</label> <label>Power</label>
<description>Power for the AC unit</description> <description>Power for the AC unit</description>
<category>Switch</category> <category>Switch</category>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-settemp"> <channel-type id="acunit-settemp">
@ -102,6 +103,7 @@
<description>The set point temperature</description> <description>The set point temperature</description>
<category>Temperature</category> <category>Temperature</category>
<state pattern="%.1f %unit%" step="0.5"/> <state pattern="%.1f %unit%" step="0.5"/>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-indoortemp"> <channel-type id="acunit-indoortemp">
@ -141,6 +143,7 @@
<option value="FAN">fan</option> <option value="FAN">fan</option>
</options> </options>
</state> </state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-homekitmode"> <channel-type id="acunit-homekitmode">
@ -155,6 +158,7 @@
<option value="off">Off</option> <option value="off">Off</option>
</options> </options>
</state> </state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-fan"> <channel-type id="acunit-fan">
@ -172,6 +176,7 @@
<option value="LEVEL_5">level 5</option> <option value="LEVEL_5">level 5</option>
</options> </options>
</state> </state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-fandir"> <channel-type id="acunit-fandir">
@ -186,6 +191,7 @@
<option value="VERTICAL_AND_HORIZONTAL">vertical and horizontal</option> <option value="VERTICAL_AND_HORIZONTAL">vertical and horizontal</option>
</options> </options>
</state> </state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-cmpfrequency" advanced="true"> <channel-type id="acunit-cmpfrequency" advanced="true">
@ -205,12 +211,14 @@
<option value="POWERFUL">Powerful</option> <option value="POWERFUL">Powerful</option>
</options> </options>
</state> </state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-streamer" advanced="true"> <channel-type id="acunit-streamer" advanced="true">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Streamer</label> <label>Streamer</label>
<description>Streamer Mode</description> <description>Streamer Mode</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-energyheatingtoday" advanced="true"> <channel-type id="acunit-energyheatingtoday" advanced="true">
@ -442,6 +450,7 @@
<option value="MANUAL">Manual</option> <option value="MANUAL">Manual</option>
</options> </options>
</state> </state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-demandcontrolmaxpower" advanced="true"> <channel-type id="acunit-demandcontrolmaxpower" advanced="true">
<item-type>Dimmer</item-type> <item-type>Dimmer</item-type>
@ -449,65 +458,76 @@
<description>The maximum power for demand control in percent. Allowed range is between 40% and 100% in increments of <description>The maximum power for demand control in percent. Allowed range is between 40% and 100% in increments of
5%.</description> 5%.</description>
<state pattern="%d %%" min="40" max="100" step="5"></state> <state pattern="%d %%" min="40" max="100" step="5"></state>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="acunit-demandcontrolschedule" advanced="true"> <channel-type id="acunit-demandcontrolschedule" advanced="true">
<item-type>String</item-type> <item-type>String</item-type>
<label>Demand Control Schedule</label> <label>Demand Control Schedule</label>
<description>The demand control schedule in JSON format.</description> <description>The demand control schedule in JSON format.</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-fan"> <channel-type id="airbase-acunit-fan">
<item-type>String</item-type> <item-type>String</item-type>
<label>Fan</label> <label>Fan</label>
<description>Current fan speed setting of the AC unit</description> <description>Current fan speed setting of the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone1"> <channel-type id="airbase-acunit-zone1">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 1</label> <label>Zone 1</label>
<description>Zone 1 for the AC unit</description> <description>Zone 1 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone2"> <channel-type id="airbase-acunit-zone2">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 2</label> <label>Zone 2</label>
<description>Zone 2 for the AC unit</description> <description>Zone 2 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone3"> <channel-type id="airbase-acunit-zone3">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 3</label> <label>Zone 3</label>
<description>Zone 3 for the AC unit</description> <description>Zone 3 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone4"> <channel-type id="airbase-acunit-zone4">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 4</label> <label>Zone 4</label>
<description>Zone 4 for the AC unit</description> <description>Zone 4 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone5"> <channel-type id="airbase-acunit-zone5">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 5</label> <label>Zone 5</label>
<description>Zone 5 for the AC unit</description> <description>Zone 5 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone6"> <channel-type id="airbase-acunit-zone6">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 6</label> <label>Zone 6</label>
<description>Zone 6 for the AC unit</description> <description>Zone 6 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone7"> <channel-type id="airbase-acunit-zone7">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 7</label> <label>Zone 7</label>
<description>Zone 7 for the AC unit</description> <description>Zone 7 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
<channel-type id="airbase-acunit-zone8"> <channel-type id="airbase-acunit-zone8">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>Zone 8</label> <label>Zone 8</label>
<description>Zone 8 for the AC unit</description> <description>Zone 8 for the AC unit</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type> </channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>