2 * Copyright (c) 2010-2022 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.Random;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Executor;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
24 import javax.measure.quantity.Temperature;
25 import javax.measure.quantity.Time;
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.bluetooth.BluetoothCharacteristic;
31 import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState;
32 import org.openhab.binding.bluetooth.ConnectedBluetoothHandler;
33 import org.openhab.binding.bluetooth.daikinmadoka.DaikinMadokaBindingConstants;
34 import org.openhab.binding.bluetooth.daikinmadoka.internal.BRC1HUartProcessor;
35 import org.openhab.binding.bluetooth.daikinmadoka.internal.DaikinMadokaConfiguration;
36 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
37 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
38 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.FanSpeed;
39 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.OperationMode;
40 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaSettings;
41 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.BRC1HCommand;
42 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.DisableCleanFilterIndicatorCommand;
43 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.EnterPrivilegedModeCommand;
44 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetCleanFilterIndicatorCommand;
45 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetEyeBrightnessCommand;
46 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetFanspeedCommand;
47 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetIndoorOutoorTemperatures;
48 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationHoursCommand;
49 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationmodeCommand;
50 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetPowerstateCommand;
51 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetSetpointCommand;
52 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetVersionCommand;
53 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResetCleanFilterTimerCommand;
54 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResponseListener;
55 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetEyeBrightnessCommand;
56 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetFanspeedCommand;
57 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetOperationmodeCommand;
58 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetPowerstateCommand;
59 import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetSetpointCommand;
60 import org.openhab.core.common.NamedThreadFactory;
61 import org.openhab.core.library.types.DecimalType;
62 import org.openhab.core.library.types.OnOffType;
63 import org.openhab.core.library.types.PercentType;
64 import org.openhab.core.library.types.QuantityType;
65 import org.openhab.core.library.types.StringType;
66 import org.openhab.core.thing.ChannelUID;
67 import org.openhab.core.thing.Thing;
68 import org.openhab.core.types.Command;
69 import org.openhab.core.types.RefreshType;
70 import org.openhab.core.types.State;
71 import org.openhab.core.types.UnDefType;
72 import org.openhab.core.util.HexUtils;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
77 * The {@link DaikinMadokaHandler} is responsible for handling commands, which are
78 * sent to one of the channels as well as updating channel values.
80 * @author Benjamin Lafois - Initial contribution
83 public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements ResponseListener {
85 private final Logger logger = LoggerFactory.getLogger(DaikinMadokaHandler.class);
87 private @Nullable DaikinMadokaConfiguration config;
89 private @Nullable ExecutorService commandExecutor;
91 private @Nullable ScheduledFuture<?> refreshJob;
93 // UART Processor is in charge of reassembling chunks
94 private BRC1HUartProcessor uartProcessor = new BRC1HUartProcessor(this);
96 private volatile @Nullable BRC1HCommand currentCommand = null;
98 private MadokaSettings madokaSettings = new MadokaSettings();
100 public DaikinMadokaHandler(Thing thing) {
105 public void initialize() {
108 logger.debug("[{}] Start initializing!", super.thing.getUID().getId());
110 // Load Configuration
111 config = getConfigAs(DaikinMadokaConfiguration.class);
112 DaikinMadokaConfiguration c = config;
114 logger.debug("[{}] Parameter value [refreshInterval]: {}", super.thing.getUID().getId(), c.refreshInterval);
115 logger.debug("[{}] Parameter value [commandTimeout]: {}", super.thing.getUID().getId(), c.commandTimeout);
117 if (getBridge() == null) {
118 logger.debug("[{}] Bridge is null. Exiting.", super.thing.getUID().getId());
122 this.commandExecutor = Executors
123 .newSingleThreadExecutor(new NamedThreadFactory(thing.getUID().getAsString(), true));
125 this.refreshJob = scheduler.scheduleWithFixedDelay(() -> {
126 // It is useless to refresh version all the time ! Just once.
127 if (this.madokaSettings.getCommunicationControllerVersion() == null
128 || this.madokaSettings.getRemoteControllerVersion() == null) {
129 submitCommand(new GetVersionCommand());
131 submitCommand(new GetIndoorOutoorTemperatures());
132 submitCommand(new GetOperationmodeCommand());
133 submitCommand(new GetPowerstateCommand()); // always keep the "GetPowerState" aftern the "GetOperationMode"
134 submitCommand(new GetSetpointCommand());
135 submitCommand(new GetFanspeedCommand());
136 submitCommand(new GetCleanFilterIndicatorCommand());
139 // As it is a complex operation - it has been extracted to a method.
140 retrieveOperationHours();
141 } catch (InterruptedException e) {
142 // The thread wants to exit!
146 submitCommand(new GetEyeBrightnessCommand());
147 }, new Random().nextInt(30), c.refreshInterval, TimeUnit.SECONDS); // We introduce a random start time, it
148 // avoids when having multiple devices to
149 // have the commands sent simultaneously.
152 private void retrieveOperationHours() throws InterruptedException {
153 // This one is special - and MUST be ran twice, after being in priv mode
154 // run it once an hour is sufficient... TODO
155 submitCommand(new EnterPrivilegedModeCommand());
156 submitCommand(new GetOperationHoursCommand());
157 // a 1second+ delay is necessary
160 submitCommand(new GetOperationHoursCommand());
164 public void dispose() {
165 logger.debug("[{}] dispose()", super.thing.getUID().getId());
168 dispose(commandExecutor);
169 dispose(currentCommand);
171 // Unsubscribe to characteristic notifications
172 if (this.device != null) {
173 BluetoothCharacteristic charNotif = this.device
174 .getCharacteristic(DaikinMadokaBindingConstants.CHAR_NOTIF_UUID);
176 if (charNotif != null) {
178 BluetoothCharacteristic c = charNotif;
179 this.device.disableNotifications(c);
186 private static void dispose(@Nullable ExecutorService executor) {
187 if (executor != null) {
188 executor.shutdownNow();
192 private static void dispose(@Nullable ScheduledFuture<?> future) {
193 if (future != null) {
198 private static void dispose(@Nullable BRC1HCommand command) {
199 if (command != null) {
200 // even if it already completed it doesn't really matter.
201 // on the off chance that the commandExecutor is waiting on the command, we can wake it up and cause it to
203 command.setState(BRC1HCommand.State.FAILED);
208 public void handleCommand(ChannelUID channelUID, Command command) {
209 logger.debug("[{}] Channel: {}, Command: {}", super.thing.getUID().getId(), channelUID, command);
211 if (command instanceof RefreshType) {
212 // The refresh commands are not supported in query mode.
213 // The binding will notify updates on channels
217 switch (channelUID.getId()) {
218 case DaikinMadokaBindingConstants.CHANNEL_ID_CLEAN_FILTER_INDICATOR:
219 OnOffType cleanFilterOrder = (OnOffType) command;
220 if (cleanFilterOrder == OnOffType.OFF) {
221 resetCleanFilterIndicator();
224 case DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT:
226 QuantityType<Temperature> setpoint = (QuantityType<Temperature>) command;
227 submitCommand(new SetSetpointCommand(setpoint, setpoint));
228 } catch (Exception e) {
229 logger.warn("Data received is not a valid temperature.", e);
232 case DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS:
234 logger.debug("Set eye brightness with value {}, {}", command.getClass().getName(), command);
235 PercentType p = (PercentType) command;
236 submitCommand(new SetEyeBrightnessCommand(p));
237 } catch (Exception e) {
238 logger.warn("Data received is not a valid Eye Brightness status", e);
241 case DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS:
243 OnOffType oot = (OnOffType) command;
244 submitCommand(new SetPowerstateCommand(oot));
245 } catch (Exception e) {
246 logger.warn("Data received is not a valid on/off status", e);
249 case DaikinMadokaBindingConstants.CHANNEL_ID_FAN_SPEED:
251 DecimalType fanSpeed = (DecimalType) command;
252 FanSpeed fs = FanSpeed.valueOf(fanSpeed.intValue());
253 submitCommand(new SetFanspeedCommand(fs, fs));
254 } catch (Exception e) {
255 logger.warn("Data received is not a valid FanSpeed status", e);
258 case DaikinMadokaBindingConstants.CHANNEL_ID_OPERATION_MODE:
260 StringType operationMode = (StringType) command;
261 OperationMode m = OperationMode.valueOf(operationMode.toFullString());
263 submitCommand(new SetOperationmodeCommand(m));
264 } catch (Exception e) {
265 logger.warn("Data received is not a valid OPERATION MODE", e);
268 case DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE:
270 // Homebridge are discrete value different from Daikin
275 DecimalType homebridgeMode = (DecimalType) command;
276 switch (homebridgeMode.intValue()) {
278 submitCommand(new SetPowerstateCommand(OnOffType.OFF));
281 submitCommand(new SetOperationmodeCommand(OperationMode.HEAT));
282 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
283 submitCommand(new SetPowerstateCommand(OnOffType.ON));
287 submitCommand(new SetOperationmodeCommand(OperationMode.COOL));
288 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
289 submitCommand(new SetPowerstateCommand(OnOffType.ON));
293 submitCommand(new SetOperationmodeCommand(OperationMode.AUTO));
294 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
295 submitCommand(new SetPowerstateCommand(OnOffType.ON));
298 default: // Invalid Value - in case of new FW
299 logger.warn("Invalid value received for channel {}. Ignoring.", channelUID);
302 } catch (Exception e) {
303 logger.warn("Data received is not a valid HOMEBRIDGE OPERATION MODE", e);
306 case DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE:
308 StringType homekitOperationMode = (StringType) command;
310 switch (homekitOperationMode.toString()) {
312 submitCommand(new SetPowerstateCommand(OnOffType.OFF));
315 submitCommand(new SetOperationmodeCommand(OperationMode.COOL));
316 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
317 submitCommand(new SetPowerstateCommand(OnOffType.ON));
321 submitCommand(new SetOperationmodeCommand(OperationMode.HEAT));
322 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
323 submitCommand(new SetPowerstateCommand(OnOffType.ON));
327 submitCommand(new SetOperationmodeCommand(OperationMode.AUTO));
328 if (madokaSettings.getOnOffState() == OnOffType.OFF) {
329 submitCommand(new SetPowerstateCommand(OnOffType.ON));
335 } catch (Exception e) {
336 logger.info("Error while setting mode through HomeKIt received Mode");
344 * 2 actions need to be done: disable the notification AND reset the filter timer
346 private void resetCleanFilterIndicator() {
347 logger.debug("[{}] resetCleanFilterIndicator()", super.thing.getUID().getId());
348 submitCommand(new DisableCleanFilterIndicatorCommand());
349 submitCommand(new ResetCleanFilterTimerCommand());
353 public void onCharacteristicUpdate(BluetoothCharacteristic characteristic, byte[] msgBytes) {
354 if (logger.isDebugEnabled()) {
355 logger.debug("[{}] onCharacteristicUpdate({})", super.thing.getUID().getId(),
356 HexUtils.bytesToHex(msgBytes));
358 super.onCharacteristicUpdate(characteristic, msgBytes);
360 // Check that arguments are valid.
361 if (characteristic.getUuid() == null) {
365 // We are only interested in the Notify Characteristic of UART service
366 if (!characteristic.getUuid().equals(DaikinMadokaBindingConstants.CHAR_NOTIF_UUID)) {
370 // A message cannot have a 0-byte length
371 if (msgBytes.length == 0) {
375 this.uartProcessor.chunkReceived(msgBytes);
378 private void submitCommand(BRC1HCommand command) {
379 Executor executor = commandExecutor;
381 if (executor != null) {
382 executor.execute(() -> processCommand(command));
386 private void processCommand(BRC1HCommand command) {
387 logger.debug("[{}] ProcessCommand {}", super.thing.getUID().getId(), command.getClass().getSimpleName());
390 currentCommand = command;
391 uartProcessor.abandon();
393 if (device == null || device.getConnectionState() != ConnectionState.CONNECTED) {
394 logger.debug("Unable to send command {} to device {}: not connected",
395 command.getClass().getSimpleName(), address);
396 command.setState(BRC1HCommand.State.FAILED);
400 if (!device.isServicesDiscovered()) {
401 logger.debug("Unable to send command {} to device {}: services not resolved",
402 command.getClass().getSimpleName(), device.getAddress());
403 command.setState(BRC1HCommand.State.FAILED);
407 BluetoothCharacteristic charWrite = device
408 .getCharacteristic(DaikinMadokaBindingConstants.CHAR_WRITE_WITHOUT_RESPONSE_UUID);
409 if (charWrite == null) {
410 logger.warn("Unable to execute {}. Characteristic '{}' could not be found.",
411 command.getClass().getSimpleName(),
412 DaikinMadokaBindingConstants.CHAR_WRITE_WITHOUT_RESPONSE_UUID);
413 command.setState(BRC1HCommand.State.FAILED);
417 BluetoothCharacteristic charNotif = this.device
418 .getCharacteristic(DaikinMadokaBindingConstants.CHAR_NOTIF_UUID);
420 if (charNotif != null) {
421 device.enableNotifications(charNotif);
424 // Commands can be composed of multiple chunks
425 for (byte[] chunk : command.getRequest()) {
426 command.setState(BRC1HCommand.State.ENQUEUED);
427 for (int i = 0; i < DaikinMadokaBindingConstants.WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
429 device.writeCharacteristic(charWrite, chunk).get(100, TimeUnit.MILLISECONDS);
430 } catch (InterruptedException ex) {
432 } catch (ExecutionException ex) {
433 logger.debug("Error while writing message {}: {}", command.getClass().getSimpleName(),
437 } catch (TimeoutException ex) {
441 command.setState(BRC1HCommand.State.SENT);
446 if (command.getState() == BRC1HCommand.State.SENT && this.config != null) {
447 if (!command.awaitStateChange(this.config.commandTimeout, TimeUnit.MILLISECONDS,
448 BRC1HCommand.State.SUCCEEDED, BRC1HCommand.State.FAILED)) {
449 logger.debug("[{}] Command {} to device {} timed out", super.thing.getUID().getId(), command,
450 device.getAddress());
451 command.setState(BRC1HCommand.State.FAILED);
454 } catch (Exception e) {
455 currentCommand = null;
456 // Let the exception bubble the stack!
457 throw new RuntimeException(e);
462 } catch (InterruptedException e) {
463 Thread.currentThread().interrupt();
468 * When the method is triggered, it means that all message chunks have been received, re-assembled in the right
469 * order and that the payload is ready to be processed.
472 public void receivedResponse(byte[] response) {
473 logger.debug("Received Response");
474 BRC1HCommand command = currentCommand;
476 if (command == null) {
477 if (logger.isDebugEnabled()) {
478 logger.debug("No command present to handle response {}", HexUtils.bytesToHex(response));
482 command.handleResponse(scheduler, this, MadokaMessage.parse(response));
483 } catch (MadokaParsingException e) {
484 logger.debug("Response message could not be parsed correctly ({}): {}. Reason: {}",
485 command.getClass().getSimpleName(), HexUtils.bytesToHex(response), e.getMessage());
491 public void receivedResponse(GetVersionCommand command) {
492 String commCtrlVers = command.getCommunicationControllerVersion();
493 if (commCtrlVers != null) {
494 this.madokaSettings.setCommunicationControllerVersion(commCtrlVers);
495 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_COMMUNICATION_CONTROLLER_VERSION,
496 new StringType(commCtrlVers));
499 String remoteCtrlVers = command.getRemoteControllerVersion();
500 if (remoteCtrlVers != null) {
501 this.madokaSettings.setRemoteControllerVersion(remoteCtrlVers);
502 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_REMOTE_CONTROLLER_VERSION,
503 new StringType(remoteCtrlVers));
508 public void receivedResponse(GetFanspeedCommand command) {
509 if (command.getCoolingFanSpeed() == null || command.getHeatingFanSpeed() == null) {
513 // We need the current operation mode to determine which Fan Speed we use (cooling or heating)
514 OperationMode operationMode = this.madokaSettings.getOperationMode();
515 if (operationMode == null) {
521 switch (operationMode) {
523 logger.debug("In AutoMode, CoolingFanSpeed = {}, HeatingFanSpeed = {}", command.getCoolingFanSpeed(),
524 command.getHeatingFanSpeed());
525 fs = command.getHeatingFanSpeed();
528 fs = command.getHeatingFanSpeed();
531 fs = command.getCoolingFanSpeed();
541 // No need to re-set if it is the same value
542 if (fs.equals(this.madokaSettings.getFanspeed())) {
546 this.madokaSettings.setFanspeed(fs);
547 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_FAN_SPEED, new DecimalType(fs.value()));
551 public void receivedResponse(GetSetpointCommand command) {
552 if (command.getCoolingSetpoint() == null || command.getHeatingSetpoint() == null) {
556 // We need the current operation mode to determine which Fan Speed we use (cooling or heating)
557 OperationMode operationMode = this.madokaSettings.getOperationMode();
558 if (operationMode == null) {
562 QuantityType<Temperature> sp;
564 switch (operationMode) {
566 logger.debug("In AutoMode, CoolingSetpoint = {}, HeatingSetpoint = {}", command.getCoolingSetpoint(),
567 command.getHeatingSetpoint());
568 sp = command.getHeatingSetpoint();
571 sp = command.getHeatingSetpoint();
574 sp = command.getCoolingSetpoint();
584 // No need to re-set if it is the same value
585 if (sp.equals(this.madokaSettings.getSetpoint())) {
589 this.madokaSettings.setSetpoint(sp);
591 QuantityType<Temperature> dt = this.madokaSettings.getSetpoint();
593 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT, dt);
598 public void receivedResponse(GetOperationmodeCommand command) {
599 if (command.getOperationMode() == null) {
600 logger.debug("OperationMode is null.");
604 OperationMode newMode = command.getOperationMode();
606 if (newMode == null) {
610 this.madokaSettings.setOperationMode(newMode);
612 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OPERATION_MODE, new StringType(newMode.name()));
614 // For HomeKit channel, we need to map it to HomeKit supported strings
615 OnOffType ooStatus = madokaSettings.getOnOffState();
617 if (ooStatus != null && ooStatus == OnOffType.ON) {
620 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
621 new StringType("Cooling"));
622 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(2));
625 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
626 new StringType("Heating"));
627 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(1));
630 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
631 new StringType("Auto"));
632 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(3));
638 // If this is the first channel update - then we set target = current mode
639 if (this.madokaSettings.getHomekitTargetMode() == null) {
640 String newHomekitTargetStatus = null;
642 // For HomeKit channel, we need to map it to HomeKit supported strings
645 newHomekitTargetStatus = "CoolOn";
648 newHomekitTargetStatus = "HeatOn";
654 if (ooStatus != null && ooStatus == OnOffType.ON) {
655 this.madokaSettings.setHomekitTargetMode(newHomekitTargetStatus);
656 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE,
657 new StringType(newHomekitTargetStatus));
658 } else if (ooStatus != null && ooStatus == OnOffType.OFF) {
659 newHomekitTargetStatus = "Off";
660 this.madokaSettings.setHomekitTargetMode(newHomekitTargetStatus);
661 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE,
662 new StringType(newHomekitTargetStatus));
669 public void receivedResponse(GetPowerstateCommand command) {
670 if (command.isPowerState() == null) {
674 OnOffType oot = command.isPowerState() ? OnOffType.ON : OnOffType.OFF;
676 this.madokaSettings.setOnOffState(oot);
678 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS, oot);
680 if (oot.equals(OnOffType.OFF)) {
681 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
682 new StringType("Off"));
683 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE,
684 new StringType("Off"));
685 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(0));
690 public void receivedResponse(GetIndoorOutoorTemperatures command) {
691 QuantityType<Temperature> newIndoorTemp = command.getIndoorTemperature();
692 if (newIndoorTemp != null) {
693 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_TEMPERATURE, newIndoorTemp);
694 this.madokaSettings.setIndoorTemperature(newIndoorTemp);
697 QuantityType<Temperature> newOutdoorTemp = command.getOutdoorTemperature();
698 if (newOutdoorTemp == null) {
699 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OUTDOOR_TEMPERATURE, UnDefType.UNDEF);
701 this.madokaSettings.setOutdoorTemperature(newOutdoorTemp);
702 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OUTDOOR_TEMPERATURE, newOutdoorTemp);
707 public void receivedResponse(GetEyeBrightnessCommand command) {
708 PercentType eyeBrightnessTemp = command.getEyeBrightness();
709 if (eyeBrightnessTemp != null) {
710 this.madokaSettings.setEyeBrightness(eyeBrightnessTemp);
711 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS, eyeBrightnessTemp);
712 logger.debug("Notified {} channel with value {}", DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS,
718 public void receivedResponse(SetEyeBrightnessCommand command) {
719 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS, command.getEyeBrightness());
720 madokaSettings.setEyeBrightness(command.getEyeBrightness());
724 public void receivedResponse(SetPowerstateCommand command) {
725 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS, command.getPowerState());
727 madokaSettings.setOnOffState(command.getPowerState());
729 if (command.getPowerState() == OnOffType.ON) {
730 // Depending on the state
732 OperationMode operationMode = madokaSettings.getOperationMode();
733 if (operationMode == null) {
737 switch (operationMode) {
739 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
740 new StringType("Auto"));
741 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(3));
744 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
745 new StringType("Heating"));
746 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(1));
749 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
750 new StringType("Cooling"));
751 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(2));
753 default: // Other Modes are not [yet] supported
757 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEKIT_CURRENT_HEATING_COOLING_MODE,
758 new StringType("Off"));
759 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_HOMEBRIDGE_MODE, new DecimalType(0));
764 public void receivedResponse(GetOperationHoursCommand command) {
765 logger.debug("receivedResponse(GetOperationHoursCommand command)");
767 QuantityType<Time> indoorPowerHours = command.getIndoorPowerHours();
768 QuantityType<Time> indoorOperationHours = command.getIndoorOperationHours();
769 QuantityType<Time> indoorFanHours = command.getIndoorFanHours();
771 if (indoorPowerHours != null) {
772 this.madokaSettings.setIndoorPowerHours(indoorPowerHours);
773 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_POWER_HOURS, indoorPowerHours);
774 logger.debug("Notified {} channel with value {}",
775 DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_POWER_HOURS, indoorPowerHours);
778 if (indoorOperationHours != null) {
779 this.madokaSettings.setIndoorOperationHours(indoorOperationHours);
780 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_OPERATION_HOURS, indoorOperationHours);
781 logger.debug("Notified {} channel with value {}",
782 DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_OPERATION_HOURS, indoorOperationHours);
785 if (indoorFanHours != null) {
786 this.madokaSettings.setIndoorFanHours(indoorFanHours);
787 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_FAN_HOURS, indoorFanHours);
788 logger.debug("Notified {} channel with value {}", DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_FAN_HOURS,
794 public void receivedResponse(SetSetpointCommand command) {
795 // The update depends on the mode - so if not set - skip
796 OperationMode operationMode = this.madokaSettings.getOperationMode();
797 if (operationMode == null) {
801 switch (operationMode) {
803 this.madokaSettings.setSetpoint(command.getHeatingSetpoint());
806 this.madokaSettings.setSetpoint(command.getCoolingSetpoint());
809 // Here we don't really care if we are taking cooling or heating...
810 this.madokaSettings.setSetpoint(command.getCoolingSetpoint());
816 QuantityType<Temperature> dt = madokaSettings.getSetpoint();
818 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT, dt);
823 public void receivedResponse(GetCleanFilterIndicatorCommand command) {
824 Boolean indicatorStatus = command.getCleanFilterIndicator();
825 if (indicatorStatus != null) {
826 this.madokaSettings.setCleanFilterIndicator(indicatorStatus);
827 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_CLEAN_FILTER_INDICATOR,
828 indicatorStatus == true ? OnOffType.ON : OnOffType.OFF);
833 * Received response to "SetOperationmodeCommand" command
836 public void receivedResponse(SetOperationmodeCommand command) {
837 this.madokaSettings.setOperationMode(command.getOperationMode());
838 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OPERATION_MODE,
839 new StringType(command.getOperationMode().toString()));
843 * Received response to "SetFanSpeed" command
846 public void receivedResponse(SetFanspeedCommand command) {
847 // The update depends on the mode - so if not set - skip
848 OperationMode operationMode = this.madokaSettings.getOperationMode();
849 if (operationMode == null) {
854 switch (operationMode) {
856 fanSpeed = command.getHeatingFanSpeed();
857 this.madokaSettings.setFanspeed(fanSpeed);
860 fanSpeed = command.getCoolingFanSpeed();
861 this.madokaSettings.setFanspeed(fanSpeed);
864 fanSpeed = command.getCoolingFanSpeed(); // Arbitrary cooling or heating... They are the same!
865 this.madokaSettings.setFanspeed(fanSpeed);
871 updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_FAN_SPEED, new DecimalType(fanSpeed.value()));
874 private void updateStateIfLinked(String channelId, State state) {
875 ChannelUID channelUID = new ChannelUID(getThing().getUID(), channelId);
876 if (isLinked(channelUID)) {
877 updateState(channelUID, state);