2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth.daikinmadoka.handler;
15 import java.util.Arrays;
16 import java.util.concurrent.Executor;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.bluetooth.BluetoothCharacteristic;
26 import org.openhab.binding.bluetooth.BluetoothCompletionStatus;
27 import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState;
28 import org.openhab.binding.bluetooth.ConnectedBluetoothHandler;
29 import org.openhab.binding.bluetooth.daikinmadoka.DaikinMadokaBindingConstants;
30 import org.openhab.binding.bluetooth.daikinmadoka.internal.BRC1HUartProcessor;
31 import org.openhab.binding.bluetooth.daikinmadoka.internal.DaikinMadokaConfiguration;
32 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
33 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
34 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.FanSpeed;
35 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.OperationMode;
36 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaSettings;
37 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.BRC1HCommand;
38 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetFanspeedCommand;
39 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetIndoorOutoorTemperatures;
40 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationmodeCommand;
41 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetPowerstateCommand;
42 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetSetpointCommand;
43 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetVersionCommand;
44 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResponseListener;
45 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetFanspeedCommand;
46 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetOperationmodeCommand;
47 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetPowerstateCommand;
48 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetSetpointCommand;
49 import org.openhab.core.common.NamedThreadFactory;
50 import org.openhab.core.library.types.DecimalType;
51 import org.openhab.core.library.types.OnOffType;
52 import org.openhab.core.library.types.QuantityType;
53 import org.openhab.core.library.types.StringType;
54 import org.openhab.core.thing.ChannelUID;
55 import org.openhab.core.thing.Thing;
56 import org.openhab.core.types.Command;
57 import org.openhab.core.types.RefreshType;
58 import org.openhab.core.types.State;
59 import org.openhab.core.types.UnDefType;
60 import org.openhab.core.util.HexUtils;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
65 * The {@link DaikinMadokaHandler} is responsible for handling commands, which are
66 * sent to one of the channels as well as updating channel values.
68 * @author Benjamin Lafois - Initial contribution
71 public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements ResponseListener {
73 private final Logger logger = LoggerFactory.getLogger(DaikinMadokaHandler.class);
75 private @Nullable DaikinMadokaConfiguration config;
77 private @Nullable ExecutorService commandExecutor;
79 private @Nullable ScheduledFuture<?> refreshJob;
81 // UART Processor is in charge of reassembling chunks
82 private BRC1HUartProcessor uartProcessor = new BRC1HUartProcessor(this);
84 private volatile @Nullable BRC1HCommand currentCommand = null;
86 private MadokaSettings madokaSettings = new MadokaSettings();
88 public DaikinMadokaHandler(Thing thing) {
93 public void initialize() {
96 logger.debug("[{}] Start initializing!", super.thing.getUID().getId());
99 config = getConfigAs(DaikinMadokaConfiguration.class);
100 DaikinMadokaConfiguration c = config;
102 logger.debug("[{}] Parameter value [refreshInterval]: {}", super.thing.getUID().getId(), c.refreshInterval);
103 logger.debug("[{}] Parameter value [commandTimeout]: {}", super.thing.getUID().getId(), c.commandTimeout);
105 if (getBridge() == null) {
106 logger.debug("[{}] Bridge is null. Exiting.", super.thing.getUID().getId());
110 this.commandExecutor = Executors
111 .newSingleThreadExecutor(new NamedThreadFactory(thing.getUID().getAsString(), true));
113 this.refreshJob = scheduler.scheduleWithFixedDelay(() -> {
114 // It is useless to refresh version all the time ! Just once.
115 if (this.madokaSettings.getCommunicationControllerVersion() == null
116 || this.madokaSettings.getRemoteControllerVersion() == null) {
117 submitCommand(new GetVersionCommand());
119 submitCommand(new GetIndoorOutoorTemperatures());
120 submitCommand(new GetOperationmodeCommand());
121 submitCommand(new GetPowerstateCommand()); // always keep the "GetPowerState" aftern the "GetOperationMode"
122 submitCommand(new GetSetpointCommand());
123 submitCommand(new GetFanspeedCommand());
124 }, 10, c.refreshInterval, TimeUnit.SECONDS);
128 public void dispose() {
129 logger.debug("[{}] dispose()", super.thing.getUID().getId());
132 dispose(commandExecutor);
133 dispose(currentCommand);
135 // Unsubscribe to characteristic notifications
136 if (this.device != null) {
137 BluetoothCharacteristic charNotif = this.device
138 .getCharacteristic(DaikinMadokaBindingConstants.CHAR_NOTIF_UUID);
140 if (charNotif != null) {
142 BluetoothCharacteristic c = charNotif;
143 this.device.disableNotifications(c);
150 private static void dispose(@Nullable ExecutorService executor) {
151 if (executor != null) {
152 executor.shutdownNow();
156 private static void dispose(@Nullable ScheduledFuture<?> future) {
157 if (future != null) {
162 private static void dispose(@Nullable BRC1HCommand command) {
163 if (command != null) {
164 // even if it already completed it doesn't really matter.
165 // on the off chance that the commandExecutor is waiting on the command, we can wake it up and cause it to
167 command.setState(BRC1HCommand.State.FAILED);
172 public void handleCommand(ChannelUID channelUID, Command command) {
173 logger.debug("[{}] Channel: {}, Command: {}", super.thing.getUID().getId(), channelUID, command);
175 if (command instanceof RefreshType) {
176 // The refresh commands are not supported in query mode.
177 // The binding will notify updates on channels
181 switch (channelUID.getId()) {
182 case DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT:
184 QuantityType<?> setpoint = (QuantityType<?>) command;
185 DecimalType dt = new DecimalType(setpoint.intValue());
186 submitCommand(new SetSetpointCommand(dt, dt));
187 } catch (Exception e) {
188 logger.warn("Data received is not a valid temperature.", e);
191 case DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS:
193 OnOffType oot = (OnOffType) command;
194 submitCommand(new SetPowerstateCommand(oot));
195 } catch (Exception e) {
196 logger.warn("Data received is not a valid on/off status", e);
199 case DaikinMadokaBindingConstants.CHANNEL_ID_FAN_SPEED:
201 DecimalType fanSpeed = (DecimalType) command;
202 FanSpeed fs = FanSpeed.valueOf(fanSpeed.intValue());
203 submitCommand(new SetFanspeedCommand(fs, fs));
204 } catch (Exception e) {
205 logger.warn("Data received is not a valid FanSpeed status", e);
208 case DaikinMadokaBindingConstants.CHANNEL_ID_OPERATION_MODE:
210 StringType operationMode = (StringType) command;
211 OperationMode m = OperationMode.valueOf(operationMode.toFullString());
213 submitCommand(new SetOperationmodeCommand(m));
214 } catch (Exception e) {
215 logger.warn("Data received is not a valid OPERATION MODE", e);
218 case DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE:
220 // Homebridge are discrete value different from Daikin
225 DecimalType homebridgeMode = (DecimalType) command;
226 switch (homebridgeMode.intValue()) {
228 submitCommand(new SetPowerstateCommand(OnOffType.OFF));
231 submitCommand(new SetOperationmodeCommand(OperationMode.HEAT));
232 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
233 submitCommand(new SetPowerstateCommand(OnOffType.ON));
237 submitCommand(new SetOperationmodeCommand(OperationMode.COOL));
238 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
239 submitCommand(new SetPowerstateCommand(OnOffType.ON));
243 submitCommand(new SetOperationmodeCommand(OperationMode.AUTO));
244 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
245 submitCommand(new SetPowerstateCommand(OnOffType.ON));
248 default: // Invalid Value - in case of new FW
249 logger.warn("Invalid value received for channel {}. Ignoring.", channelUID);
252 } catch (Exception e) {
253 logger.warn("Data received is not a valid HOMEBRIDGE OPERATION MODE", e);
256 case DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE:
258 StringType homekitOperationMode = (StringType) command;
260 switch (homekitOperationMode.toString()) {
262 submitCommand(new SetPowerstateCommand(OnOffType.OFF));
265 submitCommand(new SetOperationmodeCommand(OperationMode.COOL));
266 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
267 submitCommand(new SetPowerstateCommand(OnOffType.ON));
271 submitCommand(new SetOperationmodeCommand(OperationMode.HEAT));
272 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
273 submitCommand(new SetPowerstateCommand(OnOffType.ON));
277 submitCommand(new SetOperationmodeCommand(OperationMode.AUTO));
278 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
279 submitCommand(new SetPowerstateCommand(OnOffType.ON));
285 } catch (Exception e) {
286 logger.info("Error while setting mode through HomeKIt received Mode");
294 public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) {
295 super.onCharacteristicUpdate(characteristic);
297 // Check that arguments are valid.
298 if (characteristic.getUuid() == null) {
302 // We are only interested in the Notify Characteristic of UART service
303 if (!characteristic.getUuid().equals(DaikinMadokaBindingConstants.CHAR_NOTIF_UUID)) {
307 // A message cannot be null or have a 0-byte length
308 byte[] msgBytes = characteristic.getByteValue();
309 if (msgBytes == null || msgBytes.length == 0) {
313 this.uartProcessor.chunkReceived(msgBytes);
316 private void submitCommand(BRC1HCommand command) {
317 Executor executor = commandExecutor;
319 if (executor != null) {
320 executor.execute(() -> processCommand(command));
324 private void processCommand(BRC1HCommand command) {
325 logger.debug("[{}] ProcessCommand {}", super.thing.getUID().getId(), command.getClass().getSimpleName());
328 currentCommand = command;
329 uartProcessor.abandon();
331 if (device == null || device.getConnectionState() != ConnectionState.CONNECTED) {
332 logger.debug("Unable to send command {} to device {}: not connected",
333 command.getClass().getSimpleName(), address);
334 command.setState(BRC1HCommand.State.FAILED);
339 logger.debug("Unable to send command {} to device {}: services not resolved",
340 command.getClass().getSimpleName(), device.getAddress());
341 command.setState(BRC1HCommand.State.FAILED);
345 BluetoothCharacteristic charWrite = device
346 .getCharacteristic(DaikinMadokaBindingConstants.CHAR_WRITE_WITHOUT_RESPONSE_UUID);
347 if (charWrite == null) {
348 logger.warn("Unable to execute {}. Characteristic '{}' could not be found.",
349 command.getClass().getSimpleName(),
350 DaikinMadokaBindingConstants.CHAR_WRITE_WITHOUT_RESPONSE_UUID);
351 command.setState(BRC1HCommand.State.FAILED);
355 BluetoothCharacteristic charNotif = this.device
356 .getCharacteristic(DaikinMadokaBindingConstants.CHAR_NOTIF_UUID);
358 if (charNotif != null) {
359 device.enableNotifications(charNotif);
362 charWrite.setValue(command.getRequest());
363 command.setState(BRC1HCommand.State.ENQUEUED);
364 device.writeCharacteristic(charWrite);
366 if (this.config != null) {
367 if (!command.awaitStateChange(this.config.commandTimeout, TimeUnit.MILLISECONDS,
368 BRC1HCommand.State.SUCCEEDED, BRC1HCommand.State.FAILED)) {
369 logger.debug("Command {} to device {} timed out", command, device.getAddress());
370 command.setState(BRC1HCommand.State.FAILED);
373 } catch (Exception e) {
374 currentCommand = null;
375 // Let the exception bubble the stack!
376 throw new RuntimeException(e);
381 } catch (InterruptedException e) {
382 Thread.currentThread().interrupt();
387 public void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic,
388 BluetoothCompletionStatus status) {
389 super.onCharacteristicWriteComplete(characteristic, status);
391 byte[] request = characteristic.getByteValue();
392 BRC1HCommand command = currentCommand;
394 if (command != null) {
395 if (!Arrays.equals(request, command.getRequest())) {
396 logger.debug("Write completed for unknown command");
401 command.setState(BRC1HCommand.State.SENT);
404 command.setState(BRC1HCommand.State.FAILED);
408 if (logger.isDebugEnabled()) {
409 logger.debug("No command found that matches request {}", HexUtils.bytesToHex(request));
415 * When the method is triggered, it means that all message chunks have been received, re-assembled in the right
416 * order and that the payload is ready to be processed.
419 public void receivedResponse(byte[] response) {
420 logger.debug("Received Response");
421 BRC1HCommand command = currentCommand;
423 if (command == null) {
424 if (logger.isDebugEnabled()) {
425 logger.debug("No command present to handle response {}", HexUtils.bytesToHex(response));
429 command.handleResponse(scheduler, this, MadokaMessage.parse(response));
430 } catch (MadokaParsingException e) {
431 logger.debug("Response message could not be parsed correctly ({}): {}. Reason: {}",
432 command.getClass().getSimpleName(), HexUtils.bytesToHex(response), e.getMessage());
438 public void receivedResponse(GetVersionCommand command) {
439 String commCtrlVers = command.getCommunicationControllerVersion();
440 if (commCtrlVers != null) {
441 this.madokaSettings.setCommunicationControllerVersion(commCtrlVers);
442 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_COMMUNICATION_CONTROLLER_VERSION,
443 new StringType(commCtrlVers));
446 String remoteCtrlVers = command.getRemoteControllerVersion();
447 if (remoteCtrlVers != null) {
448 this.madokaSettings.setRemoteControllerVersion(remoteCtrlVers);
449 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_REMOTE_CONTROLLER_VERSION,
450 new StringType(remoteCtrlVers));
455 public void receivedResponse(GetFanspeedCommand command) {
456 if (command.getCoolingFanSpeed() == null || command.getHeatingFanSpeed() == null) {
460 // We need the current operation mode to determine which Fan Speed we use (cooling or heating)
461 OperationMode operationMode = this.madokaSettings.getOperationMode();
462 if (operationMode == null) {
468 switch (operationMode) {
470 logger.debug("In AutoMode, CoolingFanSpeed = {}, HeatingFanSpeed = {}", command.getCoolingFanSpeed(),
471 command.getHeatingFanSpeed());
472 fs = command.getHeatingFanSpeed();
475 fs = command.getHeatingFanSpeed();
478 fs = command.getCoolingFanSpeed();
488 // No need to re-set if it is the same value
489 if (fs.equals(this.madokaSettings.getFanspeed())) {
493 this.madokaSettings.setFanspeed(fs);
494 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_FAN_SPEED, new DecimalType(fs.value()));
498 public void receivedResponse(GetSetpointCommand command) {
499 if (command.getCoolingSetpoint() == null || command.getHeatingSetpoint() == null) {
503 // We need the current operation mode to determine which Fan Speed we use (cooling or heating)
504 OperationMode operationMode = this.madokaSettings.getOperationMode();
505 if (operationMode == null) {
511 switch (operationMode) {
513 logger.debug("In AutoMode, CoolingSetpoint = {}, HeatingSetpoint = {}", command.getCoolingSetpoint(),
514 command.getHeatingSetpoint());
515 sp = command.getHeatingSetpoint();
518 sp = command.getHeatingSetpoint();
521 sp = command.getCoolingSetpoint();
531 // No need to re-set if it is the same value
532 if (sp.equals(this.madokaSettings.getSetpoint())) {
536 this.madokaSettings.setSetpoint(sp);
538 DecimalType dt = this.madokaSettings.getSetpoint();
540 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT, dt);
545 public void receivedResponse(GetOperationmodeCommand command) {
546 if (command.getOperationMode() == null) {
547 logger.debug("OperationMode is null.");
551 OperationMode newMode = command.getOperationMode();
553 if (newMode == null) {
557 this.madokaSettings.setOperationMode(newMode);
559 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OPERATION_MODE, new StringType(newMode.name()));
561 // For HomeKit channel, we need to map it to HomeKit supported strings
562 OnOffType ooStatus = madokaSettings.getOnOffState();
564 if (ooStatus != null && ooStatus == OnOffType.ON) {
567 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
568 new StringType("Cooling"));
569 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(2));
572 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
573 new StringType("Heating"));
574 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(1));
577 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
578 new StringType("Auto"));
579 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(3));
585 // If this is the first channel update - then we set target = current mode
586 if (this.madokaSettings.getHomekitTargetMode() == null) {
587 String newHomekitTargetStatus = null;
589 // For HomeKit channel, we need to map it to HomeKit supported strings
592 newHomekitTargetStatus = "CoolOn";
595 newHomekitTargetStatus = "HeatOn";
601 if (ooStatus != null && ooStatus == OnOffType.ON) {
602 this.madokaSettings.setHomekitTargetMode(newHomekitTargetStatus);
603 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE,
604 new StringType(newHomekitTargetStatus));
605 } else if (ooStatus != null && ooStatus == OnOffType.OFF) {
606 newHomekitTargetStatus = "Off";
607 this.madokaSettings.setHomekitTargetMode(newHomekitTargetStatus);
608 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE,
609 new StringType(newHomekitTargetStatus));
616 public void receivedResponse(GetPowerstateCommand command) {
617 if (command.isPowerState() == null) {
621 OnOffType oot = command.isPowerState() ? OnOffType.ON : OnOffType.OFF;
623 this.madokaSettings.setOnOffState(oot);
625 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS, oot);
627 if (oot.equals(OnOffType.OFF)) {
628 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
629 new StringType("Off"));
630 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE,
631 new StringType("Off"));
632 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(0));
637 public void receivedResponse(GetIndoorOutoorTemperatures command) {
638 DecimalType newIndoorTemp = command.getIndoorTemperature();
639 if (newIndoorTemp != null) {
640 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_TEMPERATURE, newIndoorTemp);
641 this.madokaSettings.setIndoorTemperature(newIndoorTemp);
644 DecimalType newOutdoorTemp = command.getOutdoorTemperature();
645 if (newOutdoorTemp == null) {
646 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OUTDOOR_TEMPERATURE, UnDefType.UNDEF);
648 this.madokaSettings.setOutdoorTemperature(newOutdoorTemp);
649 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OUTDOOR_TEMPERATURE, newOutdoorTemp);
654 public void receivedResponse(SetPowerstateCommand command) {
655 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS, command.getPowerState());
657 madokaSettings.setOnOffState(command.getPowerState());
659 if (command.getPowerState() == OnOffType.ON) {
660 // Depending on the state
662 OperationMode operationMode = madokaSettings.getOperationMode();
663 if (operationMode == null) {
667 switch (operationMode) {
669 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
670 new StringType("Auto"));
671 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(3));
674 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
675 new StringType("Heating"));
676 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(1));
679 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
680 new StringType("Cooling"));
681 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(2));
683 default: // Other Modes are not [yet] supported
687 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
688 new StringType("Off"));
689 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(0));
694 public void receivedResponse(SetSetpointCommand command) {
695 // The update depends on the mode - so if not set - skip
696 OperationMode operationMode = this.madokaSettings.getOperationMode();
697 if (operationMode == null) {
701 switch (operationMode) {
703 this.madokaSettings.setSetpoint(command.getHeatingSetpoint());
706 this.madokaSettings.setSetpoint(command.getCoolingSetpoint());
709 // Here we don't really care if we are taking cooling or heating...
710 this.madokaSettings.setSetpoint(command.getCoolingSetpoint());
716 DecimalType dt = madokaSettings.getSetpoint();
718 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT, dt);
723 * Received response to "SetOperationmodeCommand" command
726 public void receivedResponse(SetOperationmodeCommand command) {
727 this.madokaSettings.setOperationMode(command.getOperationMode());
728 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OPERATION_MODE,
729 new StringType(command.getOperationMode().toString()));
733 * Received response to "SetFanSpeed" command
736 public void receivedResponse(SetFanspeedCommand command) {
737 // The update depends on the mode - so if not set - skip
738 OperationMode operationMode = this.madokaSettings.getOperationMode();
739 if (operationMode == null) {
744 switch (operationMode) {
746 fanSpeed = command.getHeatingFanSpeed();
747 this.madokaSettings.setFanspeed(fanSpeed);
750 fanSpeed = command.getCoolingFanSpeed();
751 this.madokaSettings.setFanspeed(fanSpeed);
754 fanSpeed = command.getCoolingFanSpeed(); // Arbitrary cooling or heating... They are the same!
755 this.madokaSettings.setFanspeed(fanSpeed);
761 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_FAN_SPEED, new DecimalType(fanSpeed.value()));
764 private void updateStateIfLinked(String channelId, State state) {
765 ChannelUID channelUID = new ChannelUID(getThing().getUID(), channelId);
766 if (isLinked(channelUID)) {
767 updateState(channelUID, state);