[Daikinmadoka] New channels and fixes (#9368)
* added new channels and extra fixes Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * wip Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * added multiple channels Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * fixes after PR comments Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * added support for AUTO fan mode Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * Fixes units Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * Fix PR Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * PR fixes Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * PR fixes Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com> * Fixed copyright 2020->2021 Signed-off-by: Benjamin Lafois <benjamin.lafois@gmail.com>pull/9726/head
parent
7edcdef865
commit
cb5d659c9e
|
@ -49,10 +49,16 @@ _Note that it is planned to generate some part of this based on the XML files wi
|
|||
| commCtrlVersion | String | R | Communication Controller Firmware Version
|
||||
| remoteCtrlVersion | String | R | Remote Controller Firmware Version
|
||||
| operationMode | String | R/W | The operation mode of the AC unit. Currently supported values: HEAT, COOL.
|
||||
| fanSpeed | Number | R/W | This is a "virtual channel" : its value is calculated depending on current operation mode. It is the channel to be used to change the fan speed, whatever the current mode is. Fan speed are from 1 to 5. On BRC1H, the device supports 3 speeds: LOW (1), MEDIUM (2-4), MAX (5).
|
||||
| fanSpeed | Number | R/W | This is a "virtual channel" : its value is calculated depending on current operation mode. It is the channel to be used to change the fan speed, whatever the current mode is. Fan speed are from 1 to 5. On BRC1H, the device supports 3 speeds: LOW (1), MEDIUM (2-4), MAX (5). Some BRC1H also support an AUTO (0) mode - but not all of them support it (depending on internal unit).
|
||||
| setpoint | Number:Temperature | R/W | This is a "virtual channel" : its value is calculated depending on current operation mode. It is the channel to be used to change the setpoint, whatever the current mode is.
|
||||
| homekitCurrentHeatingCoolingMode | String | R | This channel is a "virtual channel" to be used with the HomeKit add-on to implement Thermostat thing. Values supported are the HomeKit addon ones: Off, CoolOn, HeatOn, Auto.
|
||||
| homekitTargetHeatingCoolingMode | String | R/W | This channel is a "virtual channel" to be used with the HomeKit add-on to implement Thermostat thing. Values supported are the HomeKit addon ones: Off, CoolOn, HeatOn, Auto.
|
||||
| homebridgeMode | String | R/W | This channel is a "virtual channel" to be used with external HomeBridge. Values are: Off, Heating, Cooling, Auto.
|
||||
| eyeBrightness | Dimmer | R/W | This channel allows to manipulate the Blue "Eye" indicator Brightness. Values are between 0 and 100.
|
||||
| indoorPowerHours | Number:Time | R | This channel indicates the number of hours the indoor unit has been powered (operating or not).
|
||||
| indoorOperationHours | Number:Time | R | This channel indicates the number of hours the indoor unit has been operating.
|
||||
| indoorFanHours | Number:Time | R | This channel indicates the number of hours the fan has been blowing.
|
||||
| cleanFilterIndicator | Switch | R/W | This channel indicates if the filter needs cleaning. The indicator can be reset by writing "OFF" to the channel.
|
||||
|
||||
## Full Example
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ public class DaikinMadokaBindingConstants {
|
|||
private DaikinMadokaBindingConstants() {
|
||||
}
|
||||
|
||||
public static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 3;
|
||||
|
||||
public static final ThingTypeUID THING_TYPE_BRC1H = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, "brc1h");
|
||||
|
||||
public static final String CHANNEL_ID_ONOFF_STATUS = "onOffStatus";
|
||||
|
@ -45,6 +47,13 @@ public class DaikinMadokaBindingConstants {
|
|||
public static final String CHANNEL_ID_HOMEKIT_TARGET_HEATING_COOLING_MODE = "homekitTargetHeatingCoolingMode";
|
||||
public static final String CHANNEL_ID_HOMEBRIDGE_MODE = "homebridgeMode";
|
||||
|
||||
public static final String CHANNEL_ID_EYE_BRIGHTNESS = "eyeBrightness";
|
||||
public static final String CHANNEL_ID_INDOOR_OPERATION_HOURS = "indoorOperationHours";
|
||||
public static final String CHANNEL_ID_INDOOR_POWER_HOURS = "indoorPowerHours";
|
||||
public static final String CHANNEL_ID_INDOOR_FAN_HOURS = "indoorFanHours";
|
||||
|
||||
public static final String CHANNEL_ID_CLEAN_FILTER_INDICATOR = "cleanFilterIndicator";
|
||||
|
||||
/**
|
||||
* BLUETOOTH UUID (service + chars)
|
||||
*/
|
||||
|
|
|
@ -13,12 +13,16 @@
|
|||
package org.openhab.binding.bluetooth.daikinmadoka.handler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
@ -35,13 +39,20 @@ import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaPropertie
|
|||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.OperationMode;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaSettings;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.BRC1HCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.DisableCleanFilterIndicatorCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.EnterPrivilegedModeCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetCleanFilterIndicatorCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetEyeBrightnessCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetFanspeedCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetIndoorOutoorTemperatures;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationHoursCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationmodeCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetPowerstateCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetSetpointCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetVersionCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResetCleanFilterTimerCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResponseListener;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetEyeBrightnessCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetFanspeedCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetOperationmodeCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetPowerstateCommand;
|
||||
|
@ -49,6 +60,7 @@ import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetSet
|
|||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
|
@ -121,7 +133,31 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
submitCommand(new GetPowerstateCommand()); // always keep the "GetPowerState" aftern the "GetOperationMode"
|
||||
submitCommand(new GetSetpointCommand());
|
||||
submitCommand(new GetFanspeedCommand());
|
||||
}, 10, c.refreshInterval, TimeUnit.SECONDS);
|
||||
submitCommand(new GetCleanFilterIndicatorCommand());
|
||||
|
||||
try {
|
||||
// As it is a complex operation - it has been extracted to a method.
|
||||
retrieveOperationHours();
|
||||
} catch (InterruptedException e) {
|
||||
// The thread wants to exit!
|
||||
return;
|
||||
}
|
||||
|
||||
submitCommand(new GetEyeBrightnessCommand());
|
||||
}, new Random().nextInt(30), c.refreshInterval, TimeUnit.SECONDS); // We introduce a random start time, it
|
||||
// avoids when having multiple devices to
|
||||
// have the commands sent simultaneously.
|
||||
}
|
||||
|
||||
private void retrieveOperationHours() throws InterruptedException {
|
||||
// This one is special - and MUST be ran twice, after being in priv mode
|
||||
// run it once an hour is sufficient... TODO
|
||||
submitCommand(new EnterPrivilegedModeCommand());
|
||||
submitCommand(new GetOperationHoursCommand());
|
||||
// a 1second+ delay is necessary
|
||||
Thread.sleep(1500);
|
||||
|
||||
submitCommand(new GetOperationHoursCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -179,15 +215,29 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case DaikinMadokaBindingConstants.CHANNEL_ID_CLEAN_FILTER_INDICATOR:
|
||||
OnOffType cleanFilterOrder = (OnOffType) command;
|
||||
if (cleanFilterOrder == OnOffType.OFF) {
|
||||
resetCleanFilterIndicator();
|
||||
}
|
||||
break;
|
||||
case DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT:
|
||||
try {
|
||||
QuantityType<?> setpoint = (QuantityType<?>) command;
|
||||
DecimalType dt = new DecimalType(setpoint.intValue());
|
||||
submitCommand(new SetSetpointCommand(dt, dt));
|
||||
QuantityType<Temperature> setpoint = (QuantityType<Temperature>) command;
|
||||
submitCommand(new SetSetpointCommand(setpoint, setpoint));
|
||||
} catch (Exception e) {
|
||||
logger.warn("Data received is not a valid temperature.", e);
|
||||
}
|
||||
break;
|
||||
case DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS:
|
||||
try {
|
||||
logger.debug("Set eye brightness with value {}, {}", command.getClass().getName(), command);
|
||||
PercentType p = (PercentType) command;
|
||||
submitCommand(new SetEyeBrightnessCommand(p));
|
||||
} catch (Exception e) {
|
||||
logger.warn("Data received is not a valid Eye Brightness status", e);
|
||||
}
|
||||
break;
|
||||
case DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS:
|
||||
try {
|
||||
OnOffType oot = (OnOffType) command;
|
||||
|
@ -290,8 +340,21 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 2 actions need to be done: disable the notification AND reset the filter timer
|
||||
*/
|
||||
private void resetCleanFilterIndicator() {
|
||||
logger.debug("[{}] resetCleanFilterIndicator()", super.thing.getUID().getId());
|
||||
submitCommand(new DisableCleanFilterIndicatorCommand());
|
||||
submitCommand(new ResetCleanFilterTimerCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("[{}] onCharacteristicUpdate({})", super.thing.getUID().getId(),
|
||||
HexUtils.bytesToHex(characteristic.getByteValue()));
|
||||
}
|
||||
super.onCharacteristicUpdate(characteristic);
|
||||
|
||||
// Check that arguments are valid.
|
||||
|
@ -359,14 +422,27 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
device.enableNotifications(charNotif);
|
||||
}
|
||||
|
||||
charWrite.setValue(command.getRequest());
|
||||
command.setState(BRC1HCommand.State.ENQUEUED);
|
||||
device.writeCharacteristic(charWrite);
|
||||
// Commands can be composed of multiple chunks
|
||||
for (byte[] chunk : command.getRequest()) {
|
||||
charWrite.setValue(chunk);
|
||||
command.setState(BRC1HCommand.State.ENQUEUED);
|
||||
for (int i = 0; i < DaikinMadokaBindingConstants.WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
|
||||
if (device.writeCharacteristic(charWrite)) {
|
||||
command.setState(BRC1HCommand.State.SENT);
|
||||
synchronized (command) {
|
||||
command.wait(100);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config != null) {
|
||||
if (command.getState() == BRC1HCommand.State.SENT && this.config != null) {
|
||||
if (!command.awaitStateChange(this.config.commandTimeout, TimeUnit.MILLISECONDS,
|
||||
BRC1HCommand.State.SUCCEEDED, BRC1HCommand.State.FAILED)) {
|
||||
logger.debug("Command {} to device {} timed out", command, device.getAddress());
|
||||
logger.debug("[{}] Command {} to device {} timed out", super.thing.getUID().getId(), command,
|
||||
device.getAddress());
|
||||
command.setState(BRC1HCommand.State.FAILED);
|
||||
}
|
||||
}
|
||||
|
@ -392,8 +468,13 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
BRC1HCommand command = currentCommand;
|
||||
|
||||
if (command != null) {
|
||||
if (!Arrays.equals(request, command.getRequest())) {
|
||||
logger.debug("Write completed for unknown command");
|
||||
// last chunk:
|
||||
byte[] lastChunk = command.getRequest()[command.getRequest().length - 1];
|
||||
if (!Arrays.equals(request, lastChunk)) {
|
||||
logger.debug("Write completed for a chunk, but not a complete command.");
|
||||
synchronized (command) {
|
||||
command.notify();
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (status) {
|
||||
|
@ -506,7 +587,7 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
return;
|
||||
}
|
||||
|
||||
DecimalType sp;
|
||||
QuantityType<Temperature> sp;
|
||||
|
||||
switch (operationMode) {
|
||||
case AUTO:
|
||||
|
@ -535,7 +616,7 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
|
||||
this.madokaSettings.setSetpoint(sp);
|
||||
|
||||
DecimalType dt = this.madokaSettings.getSetpoint();
|
||||
QuantityType<Temperature> dt = this.madokaSettings.getSetpoint();
|
||||
if (dt != null) {
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT, dt);
|
||||
}
|
||||
|
@ -635,13 +716,13 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
|
||||
@Override
|
||||
public void receivedResponse(GetIndoorOutoorTemperatures command) {
|
||||
DecimalType newIndoorTemp = command.getIndoorTemperature();
|
||||
QuantityType<Temperature> newIndoorTemp = command.getIndoorTemperature();
|
||||
if (newIndoorTemp != null) {
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_TEMPERATURE, newIndoorTemp);
|
||||
this.madokaSettings.setIndoorTemperature(newIndoorTemp);
|
||||
}
|
||||
|
||||
DecimalType newOutdoorTemp = command.getOutdoorTemperature();
|
||||
QuantityType<Temperature> newOutdoorTemp = command.getOutdoorTemperature();
|
||||
if (newOutdoorTemp == null) {
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_OUTDOOR_TEMPERATURE, UnDefType.UNDEF);
|
||||
} else {
|
||||
|
@ -650,6 +731,23 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetEyeBrightnessCommand command) {
|
||||
PercentType eyeBrightnessTemp = command.getEyeBrightness();
|
||||
if (eyeBrightnessTemp != null) {
|
||||
this.madokaSettings.setEyeBrightness(eyeBrightnessTemp);
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS, eyeBrightnessTemp);
|
||||
logger.debug("Notified {} channel with value {}", DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS,
|
||||
eyeBrightnessTemp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(SetEyeBrightnessCommand command) {
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_EYE_BRIGHTNESS, command.getEyeBrightness());
|
||||
madokaSettings.setEyeBrightness(command.getEyeBrightness());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(SetPowerstateCommand command) {
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_ONOFF_STATUS, command.getPowerState());
|
||||
|
@ -690,6 +788,36 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetOperationHoursCommand command) {
|
||||
logger.debug("receivedResponse(GetOperationHoursCommand command)");
|
||||
|
||||
QuantityType<Time> indoorPowerHours = command.getIndoorPowerHours();
|
||||
QuantityType<Time> indoorOperationHours = command.getIndoorOperationHours();
|
||||
QuantityType<Time> indoorFanHours = command.getIndoorFanHours();
|
||||
|
||||
if (indoorPowerHours != null) {
|
||||
this.madokaSettings.setIndoorPowerHours(indoorPowerHours);
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_POWER_HOURS, indoorPowerHours);
|
||||
logger.debug("Notified {} channel with value {}",
|
||||
DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_POWER_HOURS, indoorPowerHours);
|
||||
}
|
||||
|
||||
if (indoorOperationHours != null) {
|
||||
this.madokaSettings.setIndoorOperationHours(indoorOperationHours);
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_OPERATION_HOURS, indoorOperationHours);
|
||||
logger.debug("Notified {} channel with value {}",
|
||||
DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_OPERATION_HOURS, indoorOperationHours);
|
||||
}
|
||||
|
||||
if (indoorFanHours != null) {
|
||||
this.madokaSettings.setIndoorFanHours(indoorFanHours);
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_FAN_HOURS, indoorFanHours);
|
||||
logger.debug("Notified {} channel with value {}", DaikinMadokaBindingConstants.CHANNEL_ID_INDOOR_FAN_HOURS,
|
||||
indoorFanHours);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(SetSetpointCommand command) {
|
||||
// The update depends on the mode - so if not set - skip
|
||||
|
@ -713,12 +841,22 @@ public class DaikinMadokaHandler extends ConnectedBluetoothHandler implements Re
|
|||
return;
|
||||
}
|
||||
|
||||
DecimalType dt = madokaSettings.getSetpoint();
|
||||
QuantityType<Temperature> dt = madokaSettings.getSetpoint();
|
||||
if (dt != null) {
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_SETPOINT, dt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetCleanFilterIndicatorCommand command) {
|
||||
Boolean indicatorStatus = command.getCleanFilterIndicator();
|
||||
if (indicatorStatus != null) {
|
||||
this.madokaSettings.setCleanFilterIndicator(indicatorStatus);
|
||||
updateStateIfLinked(DaikinMadokaBindingConstants.CHANNEL_ID_CLEAN_FILTER_INDICATOR,
|
||||
indicatorStatus == true ? OnOffType.ON : OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Received response to "SetOperationmodeCommand" command
|
||||
*/
|
||||
|
|
|
@ -15,9 +15,13 @@ package org.openhab.binding.bluetooth.daikinmadoka.internal;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResponseListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* As the protocol emutes an UART communication over BLE (characteristics write/notify), this class takes care of BLE
|
||||
|
@ -28,6 +32,8 @@ import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.Respon
|
|||
@NonNullByDefault
|
||||
public class BRC1HUartProcessor {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BRC1HUartProcessor.class);
|
||||
|
||||
/**
|
||||
* Maximum number of bytes per message chunk, including headers
|
||||
*/
|
||||
|
@ -42,6 +48,8 @@ public class BRC1HUartProcessor {
|
|||
|
||||
private ResponseListener responseListener;
|
||||
|
||||
private final Lock stateLock = new ReentrantLock();
|
||||
|
||||
public BRC1HUartProcessor(ResponseListener responseListener) {
|
||||
this.responseListener = responseListener;
|
||||
}
|
||||
|
@ -78,21 +86,31 @@ public class BRC1HUartProcessor {
|
|||
}
|
||||
|
||||
public void chunkReceived(byte[] byteValue) {
|
||||
this.uartMessages.add(byteValue);
|
||||
if (isMessageComplete()) {
|
||||
byte[] fullReceivedMessage = null;
|
||||
stateLock.lock();
|
||||
try {
|
||||
this.uartMessages.add(byteValue);
|
||||
if (isMessageComplete()) {
|
||||
logger.debug("Complete message received!");
|
||||
|
||||
// Beyond this point, full message received
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
// Beyond this point, full message received
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
for (byte[] msg : uartMessages) {
|
||||
if (msg.length > 1) {
|
||||
bos.write(msg, 1, msg.length - 1);
|
||||
for (byte[] msg : uartMessages) {
|
||||
if (msg.length > 1) {
|
||||
bos.write(msg, 1, msg.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.uartMessages.clear();
|
||||
fullReceivedMessage = bos.toByteArray();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
this.uartMessages.clear();
|
||||
|
||||
this.responseListener.receivedResponse(bos.toByteArray());
|
||||
if (fullReceivedMessage != null) {
|
||||
this.responseListener.receivedResponse(fullReceivedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
*/
|
||||
package org.openhab.binding.bluetooth.daikinmadoka.internal.model;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -45,13 +46,15 @@ public class MadokaMessage {
|
|||
values = new HashMap<>();
|
||||
}
|
||||
|
||||
public static byte[] createRequest(BRC1HCommand command, MadokaValue... parameters) {
|
||||
public static byte[][] createRequest(BRC1HCommand command, MadokaValue... parameters) {
|
||||
try {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
DataOutputStream request = new DataOutputStream(output);
|
||||
|
||||
// Message Length - Computed in the end
|
||||
request.writeByte(0);
|
||||
// Chunk ID
|
||||
// request.writeByte(0);
|
||||
|
||||
// Message Length - Computed in the end - left at 0 for now
|
||||
request.writeByte(0);
|
||||
|
||||
// Command ID, coded on 3 bytes
|
||||
|
@ -70,10 +73,25 @@ public class MadokaMessage {
|
|||
}
|
||||
|
||||
// Finally, compute array size
|
||||
byte[] ret = output.toByteArray();
|
||||
ret[1] = (byte) (ret.length - 1);
|
||||
byte[] payload = output.toByteArray();
|
||||
payload[0] = (byte) (payload.length);
|
||||
|
||||
return ret;
|
||||
// Now, split in chunks
|
||||
byte[][] chunks = new byte[(int) Math.ceil(payload.length / 19.)][0];
|
||||
|
||||
ByteArrayInputStream left = new ByteArrayInputStream(payload);
|
||||
int chunkId = 0;
|
||||
while (left.available() > 0) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream chunk = new DataOutputStream(bos);
|
||||
chunk.writeByte(chunkId);
|
||||
chunk.write(left.readNBytes(19));
|
||||
|
||||
chunk.flush();
|
||||
chunks[chunkId++] = bos.toByteArray();
|
||||
}
|
||||
|
||||
return chunks;
|
||||
} catch (IOException e) {
|
||||
logger.info("Error while building request", e);
|
||||
throw new RuntimeException(e);
|
||||
|
@ -105,7 +123,13 @@ public class MadokaMessage {
|
|||
|
||||
mv = new MadokaValue();
|
||||
mv.setId(msg[i]);
|
||||
mv.setSize(Byte.toUnsignedInt(msg[i + 1]));
|
||||
|
||||
if (Byte.toUnsignedInt(msg[i + 1]) == 0xff) {
|
||||
// Specific case - msg length 0xFF. See GetOperationHousCommand
|
||||
mv.setSize(0);
|
||||
} else {
|
||||
mv.setSize(Byte.toUnsignedInt(msg[i + 1]));
|
||||
}
|
||||
|
||||
if ((i + 1 + mv.getSize()) >= msg.length) {
|
||||
throw new MadokaParsingException("Truncated message detected while parsing response value content");
|
||||
|
|
|
@ -26,7 +26,8 @@ public class MadokaProperties {
|
|||
public enum FanSpeed {
|
||||
MAX(5),
|
||||
MEDIUM(3),
|
||||
LOW(1);
|
||||
LOW(1),
|
||||
AUTO(0);
|
||||
|
||||
private int v;
|
||||
|
||||
|
@ -39,8 +40,10 @@ public class MadokaProperties {
|
|||
return MAX;
|
||||
} else if (v >= 2 && v <= 4) {
|
||||
return MEDIUM;
|
||||
} else {
|
||||
} else if (v == 1) {
|
||||
return LOW;
|
||||
} else {
|
||||
return AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,16 @@
|
|||
*/
|
||||
package org.openhab.binding.bluetooth.daikinmadoka.internal.model;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.FanSpeed;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaProperties.OperationMode;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
|
||||
/**
|
||||
* This class contains the current state of the controllerw
|
||||
|
@ -30,10 +34,10 @@ public class MadokaSettings {
|
|||
|
||||
private @Nullable OnOffType onOffState;
|
||||
|
||||
private @Nullable DecimalType setpoint;
|
||||
private @Nullable QuantityType<Temperature> setpoint;
|
||||
|
||||
private @Nullable DecimalType indoorTemperature;
|
||||
private @Nullable DecimalType outdoorTemperature;
|
||||
private @Nullable QuantityType<Temperature> indoorTemperature;
|
||||
private @Nullable QuantityType<Temperature> outdoorTemperature;
|
||||
|
||||
private @Nullable FanSpeed fanspeed;
|
||||
|
||||
|
@ -45,6 +49,21 @@ public class MadokaSettings {
|
|||
private @Nullable String communicationControllerVersion;
|
||||
private @Nullable String remoteControllerVersion;
|
||||
|
||||
private @Nullable PercentType eyeBrightness;
|
||||
private @Nullable QuantityType<Time> indoorPowerHours;
|
||||
private @Nullable QuantityType<Time> indoorOperationHours;
|
||||
private @Nullable QuantityType<Time> indoorFanHours;
|
||||
|
||||
private @Nullable Boolean cleanFilterIndicator;
|
||||
|
||||
public @Nullable Boolean getCleanFilterIndicator() {
|
||||
return cleanFilterIndicator;
|
||||
}
|
||||
|
||||
public void setCleanFilterIndicator(Boolean cleanFilterIndicator) {
|
||||
this.cleanFilterIndicator = cleanFilterIndicator;
|
||||
}
|
||||
|
||||
public @Nullable OnOffType getOnOffState() {
|
||||
return onOffState;
|
||||
}
|
||||
|
@ -53,27 +72,27 @@ public class MadokaSettings {
|
|||
this.onOffState = onOffState;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getSetpoint() {
|
||||
public @Nullable QuantityType<Temperature> getSetpoint() {
|
||||
return setpoint;
|
||||
}
|
||||
|
||||
public void setSetpoint(DecimalType setpoint) {
|
||||
public void setSetpoint(QuantityType<Temperature> setpoint) {
|
||||
this.setpoint = setpoint;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getIndoorTemperature() {
|
||||
public @Nullable QuantityType<Temperature> getIndoorTemperature() {
|
||||
return indoorTemperature;
|
||||
}
|
||||
|
||||
public void setIndoorTemperature(DecimalType indoorTemperature) {
|
||||
public void setIndoorTemperature(QuantityType<Temperature> indoorTemperature) {
|
||||
this.indoorTemperature = indoorTemperature;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getOutdoorTemperature() {
|
||||
public @Nullable QuantityType<Temperature> getOutdoorTemperature() {
|
||||
return outdoorTemperature;
|
||||
}
|
||||
|
||||
public void setOutdoorTemperature(DecimalType outdoorTemperature) {
|
||||
public void setOutdoorTemperature(QuantityType<Temperature> outdoorTemperature) {
|
||||
this.outdoorTemperature = outdoorTemperature;
|
||||
}
|
||||
|
||||
|
@ -124,4 +143,36 @@ public class MadokaSettings {
|
|||
public void setRemoteControllerVersion(String remoteControllerVersion) {
|
||||
this.remoteControllerVersion = remoteControllerVersion;
|
||||
}
|
||||
|
||||
public @Nullable PercentType getEyeBrightness() {
|
||||
return eyeBrightness;
|
||||
}
|
||||
|
||||
public void setEyeBrightness(PercentType eyeBrightness) {
|
||||
this.eyeBrightness = eyeBrightness;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getIndoorPowerHours() {
|
||||
return indoorPowerHours;
|
||||
}
|
||||
|
||||
public void setIndoorPowerHours(QuantityType<Time> indoorPowerHours) {
|
||||
this.indoorPowerHours = indoorPowerHours;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getIndoorOperationHours() {
|
||||
return indoorOperationHours;
|
||||
}
|
||||
|
||||
public void setIndoorOperationHours(QuantityType<Time> indoorOperationHours) {
|
||||
this.indoorOperationHours = indoorOperationHours;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getIndoorFanHours() {
|
||||
return indoorFanHours;
|
||||
}
|
||||
|
||||
public void setIndoorFanHours(QuantityType<Time> indoorFanHours) {
|
||||
this.indoorFanHours = indoorFanHours;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
package org.openhab.binding.bluetooth.daikinmadoka.internal.model;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
@ -62,20 +63,35 @@ public class MadokaValue {
|
|||
this.rawValue = rawValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* For backward compatibility
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getComputedValue() {
|
||||
return getComputedValue(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
public long getComputedValue(ByteOrder e) {
|
||||
byte[] v = rawValue;
|
||||
if (v != null) {
|
||||
ByteBuffer bb;
|
||||
switch (size) {
|
||||
case 1:
|
||||
return v[0];
|
||||
case 2:
|
||||
return ByteBuffer.wrap(v, 0, 2).getShort();
|
||||
bb = ByteBuffer.wrap(v, 0, 2);
|
||||
bb.order(e);
|
||||
return bb.getShort();
|
||||
case 4:
|
||||
return ByteBuffer.wrap(v, 0, 4).getInt();
|
||||
bb = ByteBuffer.wrap(v, 0, 4);
|
||||
bb.order(e);
|
||||
return bb.getInt();
|
||||
default:
|
||||
// unsupported
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public abstract class BRC1HCommand {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract byte[] getRequest();
|
||||
public abstract byte[][] getRequest();
|
||||
|
||||
/**
|
||||
* This is the command number, in the protocol
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Command used to disable the Clean Filter Indicator notification
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DisableCleanFilterIndicatorCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DisableCleanFilterIndicatorCommand.class);
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm)
|
||||
throws MadokaParsingException {
|
||||
setState(State.SUCCEEDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue mv = new MadokaValue(0x51, 1, new byte[] { (byte) 0x01 });
|
||||
return MadokaMessage.createRequest(this, mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 16928;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This command enable privileged commands on remote device
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EnterPrivilegedModeCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EnterPrivilegedModeCommand.class);
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue privilegedMode = new MadokaValue(0xfe, 1, new byte[] { (byte) 0x01 });
|
||||
return MadokaMessage.createRequest(this, privilegedMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm) {
|
||||
byte[] msg = mm.getRawMessage();
|
||||
if (logger.isDebugEnabled() && msg != null) {
|
||||
logger.debug("Got response for {} : {}", this.getClass().getSimpleName(), HexUtils.bytesToHex(msg));
|
||||
}
|
||||
|
||||
setState(State.SUCCEEDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 16658;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Command used to get the Clean Filter Indicator status
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetCleanFilterIndicatorCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GetCleanFilterIndicatorCommand.class);
|
||||
|
||||
private @Nullable Boolean cleanFilterIndicator;
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm)
|
||||
throws MadokaParsingException {
|
||||
|
||||
byte[] valueCleanFilterIndicator = mm.getValues().get(0x62).getRawValue();
|
||||
if (valueCleanFilterIndicator == null || valueCleanFilterIndicator.length != 1) {
|
||||
setState(State.FAILED);
|
||||
throw new MadokaParsingException("Incorrect clean filter indicator value");
|
||||
}
|
||||
|
||||
if ((valueCleanFilterIndicator[0] & 0x01) == 0x01) {
|
||||
this.cleanFilterIndicator = true;
|
||||
} else {
|
||||
this.cleanFilterIndicator = false;
|
||||
}
|
||||
|
||||
setState(State.SUCCEEDED);
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
}
|
||||
|
||||
public @Nullable Boolean getCleanFilterIndicator() {
|
||||
return cleanFilterIndicator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 256;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Command used to get the blue Eye brightness level
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetEyeBrightnessCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GetEyeBrightnessCommand.class);
|
||||
|
||||
private @Nullable PercentType eyeBrightness;
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm)
|
||||
throws MadokaParsingException {
|
||||
byte[] bEyeBrightness = mm.getValues().get(0x33).getRawValue();
|
||||
|
||||
if (bEyeBrightness == null || bEyeBrightness == null) {
|
||||
setState(State.FAILED);
|
||||
throw new MadokaParsingException("Incorrect eye brightness value");
|
||||
}
|
||||
|
||||
Integer iEyeBrightness = Integer.valueOf(bEyeBrightness[0]);
|
||||
|
||||
if (iEyeBrightness != null) {
|
||||
// The values accepted by the device are from 0 to 19 - integers so conversion needed for Dimmer channel
|
||||
eyeBrightness = new PercentType((int) Math.round(iEyeBrightness / 0.19));
|
||||
}
|
||||
|
||||
logger.debug("Eye Brightness: {}", eyeBrightness);
|
||||
|
||||
setState(State.SUCCEEDED);
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
// We can call the function without parameters - but it will return all the display parameters, which makes a 3
|
||||
// chunks return message. As such, specifying requested value 0x33 (eyeBrightness)
|
||||
MadokaValue mv = new MadokaValue(0x33, 1, new byte[] { (byte) 0x00 });
|
||||
return MadokaMessage.createRequest(this, mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 770;
|
||||
}
|
||||
|
||||
public @Nullable PercentType getEyeBrightness() {
|
||||
return eyeBrightness;
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ public class GetFanspeedCommand extends BRC1HCommand {
|
|||
private @Nullable FanSpeed heatingFanSpeed;
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,14 @@ package org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands;
|
|||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -33,11 +36,11 @@ public class GetIndoorOutoorTemperatures extends BRC1HCommand {
|
|||
|
||||
private final Logger logger = LoggerFactory.getLogger(GetIndoorOutoorTemperatures.class);
|
||||
|
||||
private @Nullable DecimalType indoorTemperature;
|
||||
private @Nullable DecimalType outdoorTemperature;
|
||||
private @Nullable QuantityType<Temperature> indoorTemperature;
|
||||
private @Nullable QuantityType<Temperature> outdoorTemperature;
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
|
@ -64,11 +67,11 @@ public class GetIndoorOutoorTemperatures extends BRC1HCommand {
|
|||
}
|
||||
|
||||
if (iIndoorTemperature != null) {
|
||||
indoorTemperature = new DecimalType(iIndoorTemperature);
|
||||
indoorTemperature = new QuantityType<Temperature>(iIndoorTemperature, SIUnits.CELSIUS);
|
||||
}
|
||||
|
||||
if (iOutdoorTemperature != null) {
|
||||
outdoorTemperature = new DecimalType(iOutdoorTemperature);
|
||||
outdoorTemperature = new QuantityType<Temperature>(iOutdoorTemperature, SIUnits.CELSIUS);
|
||||
}
|
||||
|
||||
logger.debug("Indoor Temp: {}", indoorTemperature);
|
||||
|
@ -78,11 +81,11 @@ public class GetIndoorOutoorTemperatures extends BRC1HCommand {
|
|||
executor.execute(() -> listener.receivedResponse(this));
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getIndoorTemperature() {
|
||||
public @Nullable QuantityType<Temperature> getIndoorTemperature() {
|
||||
return indoorTemperature;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getOutdoorTemperature() {
|
||||
public @Nullable QuantityType<Temperature> getOutdoorTemperature() {
|
||||
return outdoorTemperature;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This command returns the operating hours of internal unit
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetOperationHoursCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GetOperationHoursCommand.class);
|
||||
|
||||
private @Nullable QuantityType<Time> indoorOperationHours;
|
||||
private @Nullable QuantityType<Time> indoorFanHours;
|
||||
private @Nullable QuantityType<Time> indoorPowerHours;
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue specificUnitNumber = new MadokaValue(0x02, 1, new byte[] { (byte) 0x00 });
|
||||
MadokaValue p40 = new MadokaValue(0x40, 0, new byte[] {});
|
||||
MadokaValue p41 = new MadokaValue(0x41, 0, new byte[] {});
|
||||
MadokaValue p42 = new MadokaValue(0x42, 0, new byte[] {});
|
||||
MadokaValue p43 = new MadokaValue(0x43, 0, new byte[] {});
|
||||
MadokaValue p44 = new MadokaValue(0x44, 0, new byte[] {});
|
||||
MadokaValue p45 = new MadokaValue(0x45, 0, new byte[] {});
|
||||
MadokaValue p46 = new MadokaValue(0x46, 0, new byte[] {});
|
||||
MadokaValue p47 = new MadokaValue(0x47, 0, new byte[] {});
|
||||
MadokaValue p48 = new MadokaValue(0x48, 0, new byte[] {});
|
||||
|
||||
return MadokaMessage.createRequest(this, specificUnitNumber, p40, p41, p42, p43, p44, p45, p46, p47, p48);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm)
|
||||
throws MadokaParsingException {
|
||||
try {
|
||||
|
||||
byte[] msg = mm.getRawMessage();
|
||||
if (logger.isDebugEnabled() && msg != null) {
|
||||
logger.debug("Got response for {} : {}", this.getClass().getSimpleName(), HexUtils.bytesToHex(msg));
|
||||
}
|
||||
|
||||
// The specific GetOperationHours requires 2 consecutive runs for some reason.
|
||||
// If value size is 0, then it will be for the next query!
|
||||
if (mm.getValues().get(0x40).getSize() == 0) {
|
||||
setState(State.SUCCEEDED);
|
||||
return;
|
||||
}
|
||||
|
||||
Integer iIndoorOperationHours = (int) (mm.getValues().get(0x40).getComputedValue(ByteOrder.LITTLE_ENDIAN));
|
||||
Integer iIndoorFanHours = (int) (mm.getValues().get(0x41).getComputedValue(ByteOrder.LITTLE_ENDIAN));
|
||||
Integer iIndoorPowerHours = (int) (mm.getValues().get(0x42).getComputedValue(ByteOrder.LITTLE_ENDIAN));
|
||||
|
||||
this.indoorOperationHours = new QuantityType<Time>(iIndoorOperationHours, Units.HOUR);
|
||||
this.indoorFanHours = new QuantityType<Time>(iIndoorFanHours, Units.HOUR);
|
||||
this.indoorPowerHours = new QuantityType<Time>(iIndoorPowerHours, Units.HOUR);
|
||||
|
||||
logger.debug("indoorOperationHours: {}", indoorOperationHours);
|
||||
logger.debug("indoorFanHours: {}", indoorFanHours);
|
||||
logger.debug("indoorPowerHours: {}", indoorPowerHours);
|
||||
|
||||
setState(State.SUCCEEDED);
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
} catch (Exception e) {
|
||||
setState(State.FAILED);
|
||||
throw new MadokaParsingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 274;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getIndoorOperationHours() {
|
||||
return indoorOperationHours;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getIndoorFanHours() {
|
||||
return indoorFanHours;
|
||||
}
|
||||
|
||||
public @Nullable QuantityType<Time> getIndoorPowerHours() {
|
||||
return indoorPowerHours;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ public class GetOperationmodeCommand extends BRC1HCommand {
|
|||
private @Nullable OperationMode operationMode;
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public class GetPowerstateCommand extends BRC1HCommand {
|
|||
private @Nullable Boolean powerState;
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,14 @@ package org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands;
|
|||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -33,11 +36,11 @@ public class GetSetpointCommand extends BRC1HCommand {
|
|||
|
||||
private final Logger logger = LoggerFactory.getLogger(GetSetpointCommand.class);
|
||||
|
||||
private @Nullable DecimalType heatingSetpoint;
|
||||
private @Nullable DecimalType coolingSetpoint;
|
||||
private @Nullable QuantityType<Temperature> heatingSetpoint;
|
||||
private @Nullable QuantityType<Temperature> coolingSetpoint;
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
|
@ -48,8 +51,8 @@ public class GetSetpointCommand extends BRC1HCommand {
|
|||
Integer iHeatingSetpoint = (int) (mm.getValues().get(0x21).getComputedValue() / 128.);
|
||||
Integer iCoolingSetpoint = (int) (mm.getValues().get(0x20).getComputedValue() / 128.);
|
||||
|
||||
this.heatingSetpoint = new DecimalType(iHeatingSetpoint);
|
||||
this.coolingSetpoint = new DecimalType(iCoolingSetpoint);
|
||||
this.heatingSetpoint = new QuantityType<Temperature>(iHeatingSetpoint, SIUnits.CELSIUS);
|
||||
this.coolingSetpoint = new QuantityType<Temperature>(iCoolingSetpoint, SIUnits.CELSIUS);
|
||||
|
||||
logger.debug("heatingSetpoint: {}", heatingSetpoint);
|
||||
logger.debug("coolingSetpoint: {}", coolingSetpoint);
|
||||
|
@ -67,11 +70,11 @@ public class GetSetpointCommand extends BRC1HCommand {
|
|||
return 64;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getHeatingSetpoint() {
|
||||
public @Nullable QuantityType<Temperature> getHeatingSetpoint() {
|
||||
return heatingSetpoint;
|
||||
}
|
||||
|
||||
public @Nullable DecimalType getCoolingSetpoint() {
|
||||
public @Nullable QuantityType<Temperature> getCoolingSetpoint() {
|
||||
return coolingSetpoint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class GetVersionCommand extends BRC1HCommand {
|
|||
private @Nullable String communicationControllerVersion;
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
return MadokaMessage.createRequest(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Command used to reset the Clean Filter Indicator timer
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResetCleanFilterTimerCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ResetCleanFilterTimerCommand.class);
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm)
|
||||
throws MadokaParsingException {
|
||||
setState(State.SUCCEEDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue mv = new MadokaValue(0xFE, 1, new byte[] { (byte) 0x01 });
|
||||
return MadokaMessage.createRequest(this, mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 16928;
|
||||
}
|
||||
}
|
|
@ -44,4 +44,12 @@ public interface ResponseListener {
|
|||
public void receivedResponse(SetOperationmodeCommand command);
|
||||
|
||||
public void receivedResponse(SetFanspeedCommand command);
|
||||
|
||||
public void receivedResponse(GetOperationHoursCommand command);
|
||||
|
||||
public void receivedResponse(GetEyeBrightnessCommand command);
|
||||
|
||||
public void receivedResponse(SetEyeBrightnessCommand command);
|
||||
|
||||
public void receivedResponse(GetCleanFilterIndicatorCommand command);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal.model.commands;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaParsingException;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Command used to set the Blue Eye Brightness
|
||||
*
|
||||
* @author Benjamin Lafois - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SetEyeBrightnessCommand extends BRC1HCommand {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SetEyeBrightnessCommand.class);
|
||||
|
||||
private PercentType eyeBrightness;
|
||||
|
||||
public SetEyeBrightnessCommand(PercentType eyeBrightness) {
|
||||
this.eyeBrightness = eyeBrightness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResponse(Executor executor, ResponseListener listener, MadokaMessage mm)
|
||||
throws MadokaParsingException {
|
||||
byte[] msg = mm.getRawMessage();
|
||||
if (logger.isDebugEnabled() && msg != null) {
|
||||
logger.debug("Got response for {} : {}", this.getClass().getSimpleName(), HexUtils.bytesToHex(msg));
|
||||
}
|
||||
|
||||
setState(State.SUCCEEDED);
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] getRequest() {
|
||||
// The values accepted by the device are from 0 to 19 - integers
|
||||
byte val = (byte) Math.round(eyeBrightness.intValue() * 0.19);
|
||||
|
||||
MadokaValue mv = new MadokaValue(0x33, 1, new byte[] { val });
|
||||
return MadokaMessage.createRequest(this, mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCommandId() {
|
||||
return 17154;
|
||||
}
|
||||
|
||||
public PercentType getEyeBrightness() {
|
||||
return eyeBrightness;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param eyeBrightness a percentage - between 0 and 100
|
||||
*/
|
||||
public void setEyeBrightness(PercentType eyeBrightness) {
|
||||
this.eyeBrightness = eyeBrightness;
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ public class SetFanspeedCommand extends BRC1HCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue paramCoolingFanSpeed = new MadokaValue(0x20, 1, new byte[] { (byte) coolingFanSpeed.value() });
|
||||
MadokaValue paramHeatingFanSpeed = new MadokaValue(0x21, 1, new byte[] { (byte) heatingFanSpeed.value() });
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ public class SetOperationmodeCommand extends BRC1HCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue mv = new MadokaValue(0x20, 1, new byte[] { (byte) this.operationMode.value() });
|
||||
return MadokaMessage.createRequest(this, mv);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class SetPowerstateCommand extends BRC1HCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
MadokaValue mv = new MadokaValue(0x20, 1,
|
||||
new byte[] { (byte) (this.powerState == OnOffType.ON ? 0x01 : 0x00) });
|
||||
|
||||
|
|
|
@ -15,10 +15,12 @@ package org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -34,16 +36,16 @@ public class SetSetpointCommand extends BRC1HCommand {
|
|||
|
||||
private final Logger logger = LoggerFactory.getLogger(SetSetpointCommand.class);
|
||||
|
||||
private DecimalType coolingSetpoint;
|
||||
private DecimalType heatingSetpoint;
|
||||
private QuantityType<Temperature> coolingSetpoint;
|
||||
private QuantityType<Temperature> heatingSetpoint;
|
||||
|
||||
public SetSetpointCommand(DecimalType coolingSetpoint, DecimalType heatingSetpoint) {
|
||||
public SetSetpointCommand(QuantityType<Temperature> coolingSetpoint, QuantityType<Temperature> heatingSetpoint) {
|
||||
this.coolingSetpoint = coolingSetpoint;
|
||||
this.heatingSetpoint = heatingSetpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRequest() {
|
||||
public byte[][] getRequest() {
|
||||
byte[] heatingSetpointBytes = ByteBuffer.allocate(2).putShort((short) (128. * heatingSetpoint.shortValue()))
|
||||
.array();
|
||||
byte[] coolingSetpointBytes = ByteBuffer.allocate(2).putShort((short) (128. * coolingSetpoint.shortValue()))
|
||||
|
@ -72,11 +74,11 @@ public class SetSetpointCommand extends BRC1HCommand {
|
|||
return 16448;
|
||||
}
|
||||
|
||||
public DecimalType getCoolingSetpoint() {
|
||||
public QuantityType<Temperature> getCoolingSetpoint() {
|
||||
return coolingSetpoint;
|
||||
}
|
||||
|
||||
public DecimalType getHeatingSetpoint() {
|
||||
public QuantityType<Temperature> getHeatingSetpoint() {
|
||||
return heatingSetpoint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
<channel id="homekitCurrentHeatingCoolingMode" typeId="brc1h_homekitCurrentHeatingCoolingMode"/>
|
||||
<channel id="homekitTargetHeatingCoolingMode" typeId="brc1h_homekitTargetHeatingCoolingMode"/>
|
||||
<channel id="homebridgeMode" typeId="brc1h_homebridgeMode"/>
|
||||
<channel id="eyeBrightness" typeId="brc1h_eyeBrightness"/>
|
||||
<channel id="indoorPowerHours" typeId="brc1h_indoorPowerHours"/>
|
||||
<channel id="indoorOperationHours" typeId="brc1h_indoorOperationHours"/>
|
||||
<channel id="indoorFanHours" typeId="brc1h_indoorFanHours"/>
|
||||
<channel id="cleanFilterIndicator" typeId="brc1h_cleanFilter"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
|
@ -52,6 +57,11 @@
|
|||
<label>Unit Power Status</label>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="brc1h_cleanFilter">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Clean Filter Indicator</label>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="brc1h_indoorTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Indoor Temperature</label>
|
||||
|
@ -129,4 +139,28 @@
|
|||
</command>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="brc1h_eyeBrightness">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Eye Illumination Brightness</label>
|
||||
<state min="0" max="100" step="1" readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="brc1h_indoorPowerHours">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Number of hours system has been powered up</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="brc1h_indoorOperationHours">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Number of hours system has been operating</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="brc1h_indoorFanHours">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Number of hours fan has been operating</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
|
|
@ -14,10 +14,13 @@ package org.openhab.binding.bluetooth.daikinmadoka.internal;
|
|||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaMessage;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.MadokaValue;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetIndoorOutoorTemperatures;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationHoursCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetPowerstateCommand;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
|
||||
|
@ -30,16 +33,43 @@ public class MadokaMessageTest {
|
|||
|
||||
@Test
|
||||
public void testMessageBuildTemperature() {
|
||||
byte[] resp = MadokaMessage.createRequest(new GetIndoorOutoorTemperatures());
|
||||
assertArrayEquals(resp, new byte[] { 0x00, 0x06, 0x00, 0x01, 0x10, 0x00, 0x00 });
|
||||
byte[][] resp = new GetIndoorOutoorTemperatures().getRequest();
|
||||
assertArrayEquals(resp[0], new byte[] { 0x00, 0x06, 0x00, 0x01, 0x10, 0x00, 0x00 });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageBuildSetPower() {
|
||||
boolean powered = true;
|
||||
MadokaValue mv = new MadokaValue(0x20, 1, new byte[] { 1 });
|
||||
byte[] resp = MadokaMessage.createRequest(new SetPowerstateCommand(OnOffType.ON), mv);
|
||||
byte[][] resp = MadokaMessage.createRequest(new SetPowerstateCommand(OnOffType.ON), mv);
|
||||
assertArrayEquals(
|
||||
new byte[] { 0x00, 0x07, 0x00, 0x40, 0x20, 0x20, 0x01, (byte) (powered == true ? 0x01 : 0x00) }, resp);
|
||||
new byte[] { 0x00, 0x07, 0x00, 0x40, 0x20, 0x20, 0x01, (byte) (powered == true ? 0x01 : 0x00) },
|
||||
resp[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageBuildSize() {
|
||||
byte[][] resp = MadokaMessage.createRequest(new GetIndoorOutoorTemperatures());
|
||||
assertEquals(1, resp.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationHoursCommand() {
|
||||
byte[][] resp = new GetOperationHoursCommand().getRequest();
|
||||
assertEquals(2, resp.length);
|
||||
assertArrayEquals(new byte[] { 0x00, 0x19, 0x00, 0x01, 0x12, 0x02, 0x01, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42,
|
||||
0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00 }, resp[0]);
|
||||
assertArrayEquals(new byte[] { 0x01, 0x46, 0x00, 0x47, 0x00, 0x48, 0x00 }, resp[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseOperationHours() {
|
||||
String s = "390001120201004004DC0B00004104F40300004204642300004304000000004404000000004504000000004604000000004704000000004800";
|
||||
|
||||
MadokaValue mv = new MadokaValue(0, 4, new byte[] { (byte) 0xF4, 0x03, 0x00, 0x00 });
|
||||
// MadokaValue mv = new MadokaValue(0, 4, new byte[] { 0x00, 0x00, 0x03, (byte) 0xF4 });
|
||||
|
||||
Long v = mv.getComputedValue(ByteOrder.LITTLE_ENDIAN);
|
||||
assertEquals(1012, v);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.bluetooth.daikinmadoka.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetCleanFilterIndicatorCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetEyeBrightnessCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetFanspeedCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetIndoorOutoorTemperatures;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationHoursCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetOperationmodeCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetPowerstateCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetSetpointCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.GetVersionCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.ResponseListener;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetEyeBrightnessCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetFanspeedCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetOperationmodeCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetPowerstateCommand;
|
||||
import org.openhab.binding.bluetooth.daikinmadoka.internal.model.commands.SetSetpointCommand;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author blafois
|
||||
*
|
||||
*/
|
||||
public class UartProcessorTest implements ResponseListener {
|
||||
|
||||
private boolean completed = false;
|
||||
|
||||
@Test
|
||||
public void testUartProcessor() {
|
||||
BRC1HUartProcessor processor = new BRC1HUartProcessor(this);
|
||||
|
||||
processor.chunkReceived(
|
||||
new byte[] { 0x01, 0x1F, 0x01, 0x03, 0x20, 0x01, 0x04, 0x21, 0x01, 0x01, 0x30, 0x01, 0x00 });
|
||||
|
||||
processor.chunkReceived(new byte[] { 0x00, 0x1F, 0x00, 0x00, 0x30, 0x10, 0x01, 0x00, 0x13, 0x01, 0x1F, 0x15,
|
||||
0x01, 0x10, 0x16, 0x01, 0x12, 0x17, 0x01, 0x20 });
|
||||
|
||||
assertTrue(completed);
|
||||
|
||||
this.completed = false;
|
||||
|
||||
processor.chunkReceived(new byte[] { 0x01, 0x01, 0x00, 0x31, 0x01, 0x01, 0x32, 0x01, 0x00, 0x40, 0x01, 0x00,
|
||||
(byte) 0xA0, 0x01, 0x10, (byte) 0xA1, 0x01, 0x10, (byte) 0xA2, 0x02 });
|
||||
assertFalse(completed);
|
||||
processor.chunkReceived(new byte[] { 0x00, 0x49, 0x00, 0x00, 0x40, 0x12, 0x01, 0x1C, 0x15, 0x01, (byte) 0xF0,
|
||||
0x20, 0x02, 0x0A, (byte) 0x80, 0x21, 0x02, 0x0A, (byte) 0x80, 0x30 });
|
||||
assertFalse(completed);
|
||||
processor.chunkReceived(new byte[] { 0x02, 0x08, 0x00, (byte) 0xA3, 0x02, 0x08, 0x00, (byte) 0xA4, 0x01, 0x11,
|
||||
(byte) 0xA5, 0x01, 0x11, (byte) 0xB0, 0x01, 0x20, (byte) 0xB1, 0x01, 0x20, (byte) 0xB2 });
|
||||
assertFalse(completed);
|
||||
processor.chunkReceived(new byte[] { 0x03, 0x02, 0x10, 0x00, (byte) 0xB3, 0x02, 0x10, 0x00, (byte) 0xB4, 0x01,
|
||||
0x17, (byte) 0xB5, 0x01, 0x17, (byte) 0xFE, 0x01, 0x02 });
|
||||
assertTrue(completed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(byte @NonNull [] bytes) {
|
||||
this.completed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetVersionCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetFanspeedCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetOperationmodeCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetPowerstateCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetSetpointCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetIndoorOutoorTemperatures command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull SetPowerstateCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull SetSetpointCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull SetOperationmodeCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull SetFanspeedCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetOperationHoursCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetEyeBrightnessCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull GetCleanFilterIndicatorCommand command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(@NonNull SetEyeBrightnessCommand command) {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue