Skip to content

Commit

Permalink
[loxone] Implementation of EIB Dimmer (openhab#10585)
Browse files Browse the repository at this point in the history
Signed-off-by: Pawel Pieczul <pieczul@gmail.com>
  • Loading branch information
ppieczul authored and thinkingstone committed Nov 7, 2021
1 parent 007da10 commit 60efeab
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 126 deletions.
5 changes: 3 additions & 2 deletions bundles/org.openhab.binding.loxone/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ The binding supports the following authentication methods, which are selected au
| Method | Miniserver Firmware | Authentication | Encryption | Requirements |
|-------------|---------------------|--------------------------------------------------------------------------------|------------|-------------------------------------------------------|
| Hash-based | 8.x | HMAC-SHA1 hash on user and password | None | None |
| Token-based | 9.x | Token acquired on the first connection and used later instead of the password. | AES-256 | JRE must have unrestricted security policy configured |
| Token-based | From 9.x | Token acquired on the first connection and used later instead of the password. | AES-256 | JRE must have unrestricted security policy configured |

For the token-based authentication, the password is required only for the first login and acquiring the token. After the token is acquired, the password is cleared in the binding configuration.

Expand Down Expand Up @@ -121,7 +121,8 @@ Currently supported controls are presented in the table below.
| | | `String` - list of alarm sensors separated with `|` | Read-only channel |
| | | `Switch` - acknowledge the alarm - pushbutton | `OnOffType.ON` - acknowledge alarm |
| ColorPickerV2 | [RGBW 24v Dimmer Tree](https://www.loxone.com/enen/kb/rgbw-24v-dimmer-tree/) | `Color` | `HSBType` - sets the color of the light, `DecimalType` and `PercentType` - sets the brightness, `IncreaseDecreaseType.*` - increases/decreases the brightness, `OnOffType.*` - switches light on/off |
| Dimmer | [Dimmer](https://www.loxone.com/enen/kb/dimmer/) | `Dimmer` | `OnOffType.*`, `PercentType` |
| Dimmer | [Dimmer](https://www.loxone.com/enen/kb/dimmer/) | `Dimmer` | `OnOffType.*`, `PercentType`, `IncreaseDecreaseType.*` |
| EIBDimmer | EIB Dimmer (undocumented) | `Dimmer` | `OnOffType.*`, `PercentType`, `IncreaseDecreaseType.*` |
| InfoOnlyAnalog | Analog [virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) (virtual state) | `Number` | Read-only channel |
| InfoOnlyDigital | Digital [virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) (virtual state) | `String` | Read-only channel |
| IRoomControllerV2 | [Intelligent Room Controller V2](https://www.loxone.com/enen/kb/irc-v2/) | `Number` - active mode | Read-only channel |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,7 @@
*/
package org.openhab.binding.loxone.internal.controls;

import static org.openhab.binding.loxone.internal.LxBindingConstants.*;

import java.io.IOException;

import org.openhab.binding.loxone.internal.types.LxCategory;
import org.openhab.binding.loxone.internal.types.LxTags;
import org.openhab.binding.loxone.internal.types.LxUuid;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;

/**
* A dimmer type of control on Loxone Miniserver.
Expand All @@ -36,7 +25,7 @@
* @author Stephan Brunner - initial contribution
*
*/
class LxControlDimmer extends LxControl {
class LxControlDimmer extends LxControlEIBDimmer {

static class Factory extends LxControlInstance {
@Override
Expand All @@ -51,118 +40,28 @@ String getType() {
}

/**
* States
* States additionally to EIBDimmer
*/
private static final String STATE_POSITION = "position";
private static final String STATE_MIN = "min";
private static final String STATE_MAX = "max";
private static final String STATE_STEP = "step";

/**
* Command string used to set the dimmer ON
*/
private static final String CMD_ON = "On";
/**
* Command string used to set the dimmer to OFF
*/
private static final String CMD_OFF = "Off";

private LxControlDimmer(LxUuid uuid) {
super(uuid);
}

@Override
public void initialize(LxControlConfig config) {
super.initialize(config);
LxCategory category = getCategory();
if (category != null && category.getType() == LxCategory.CategoryType.LIGHTS) {
tags.addAll(LxTags.LIGHTING);
}
addChannel("Dimmer", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_DIMMER), defaultChannelLabel,
"Dimmer", tags, this::handleCommands, this::getChannelState);
}

private void handleCommands(Command command) throws IOException {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
sendAction(CMD_ON);
} else {
sendAction(CMD_OFF);
}
} else if (command instanceof PercentType) {
PercentType percentCmd = (PercentType) command;
setPosition(percentCmd.doubleValue());
} else if (command instanceof IncreaseDecreaseType) {
Double value = getStateDoubleValue(STATE_POSITION);
Double min = getStateDoubleValue(STATE_MIN);
Double max = getStateDoubleValue(STATE_MAX);
Double step = getStateDoubleValue(STATE_STEP);
if (value != null && max != null && min != null && step != null && min >= 0 && max >= 0 && max > min) {
if ((IncreaseDecreaseType) command == IncreaseDecreaseType.INCREASE) {
value += step;
if (value > max) {
value = max;
}
} else {
value -= step;
if (value < min) {
value = min;
}
}
sendAction(value.toString());
}
}
Double getMin() {
return getStateDoubleValue(STATE_MIN);
}

private PercentType getChannelState() {
Double value = mapLoxoneToOH(getStateDoubleValue(STATE_POSITION));
if (value != null && value >= 0 && value <= 100) {
return new PercentType(value.intValue());
}
return null;
}

/**
* Sets the current position of the dimmer
*
* @param position position to move to (0-100, 0 - full off, 100 - full on)
* @throws IOException error communicating with the Miniserver
*/
private void setPosition(Double position) throws IOException {
Double loxonePosition = mapOHToLoxone(position);
if (loxonePosition != null) {
sendAction(loxonePosition.toString());
}
}

private Double mapLoxoneToOH(Double loxoneValue) {
if (loxoneValue != null) {
// 0 means turn dimmer off, any value above zero should be mapped from min-max range
if (Double.compare(loxoneValue, 0.0) == 0) {
return 0.0;
}
Double max = getStateDoubleValue(STATE_MAX);
Double min = getStateDoubleValue(STATE_MIN);
if (max != null && min != null && max > min && min >= 0 && max >= 0) {
return 100 * (loxoneValue - min) / (max - min);
}
}
return null;
@Override
Double getMax() {
return getStateDoubleValue(STATE_MAX);
}

private Double mapOHToLoxone(Double ohValue) {
if (ohValue != null) {
// 0 means turn dimmer off, any value above zero should be mapped to min-max range
if (Double.compare(ohValue, 0.0) == 0) {
return 0.0;
}
Double max = getStateDoubleValue(STATE_MAX);
Double min = getStateDoubleValue(STATE_MIN);
if (max != null && min != null) {
double value = min + ohValue * (max - min) / 100;
return value; // no rounding to integer value is needed as loxone is accepting floating point values
}
}
return null;
@Override
Double getStep() {
return getStateDoubleValue(STATE_STEP);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/**
* 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.loxone.internal.controls;

import static org.openhab.binding.loxone.internal.LxBindingConstants.*;

import java.io.IOException;

import org.openhab.binding.loxone.internal.types.LxCategory;
import org.openhab.binding.loxone.internal.types.LxTags;
import org.openhab.binding.loxone.internal.types.LxUuid;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;

/**
* An EIB dimmer type of control on Loxone Miniserver.
* <p>
* This control is absent in the API documentation. It looks like it behaves like a normal Dimmer, but it is missing the
* information about min, max and step values.
*
* @author Pawel Pieczul - initial contribution
*
*/
class LxControlEIBDimmer extends LxControl {

static class Factory extends LxControlInstance {
@Override
LxControl create(LxUuid uuid) {
return new LxControlEIBDimmer(uuid);
}

@Override
String getType() {
return "eibdimmer";
}
}

/**
* States
*/
private static final String STATE_POSITION = "position";
private static final Double DEFAULT_MIN = 0.0;
private static final Double DEFAULT_MAX = 100.0;
private static final Double DEFAULT_STEP = 5.0;

/**
* Command string used to set the dimmer ON
*/
private static final String CMD_ON = "On";
/**
* Command string used to set the dimmer to OFF
*/
private static final String CMD_OFF = "Off";

LxControlEIBDimmer(LxUuid uuid) {
super(uuid);
}

@Override
public void initialize(LxControlConfig config) {
super.initialize(config);
LxCategory category = getCategory();
if (category != null && category.getType() == LxCategory.CategoryType.LIGHTS) {
tags.addAll(LxTags.LIGHTING);
}
addChannel("Dimmer", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_DIMMER), defaultChannelLabel,
"Dimmer", tags, this::handleCommands, this::getChannelState);
}

Double getMin() {
return DEFAULT_MIN;
}

Double getMax() {
return DEFAULT_MAX;
}

Double getStep() {
return DEFAULT_STEP;
}

private void handleCommands(Command command) throws IOException {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
sendAction(CMD_ON);
} else {
sendAction(CMD_OFF);
}
} else if (command instanceof PercentType) {
PercentType percentCmd = (PercentType) command;
setPosition(percentCmd.doubleValue());
} else if (command instanceof IncreaseDecreaseType) {
Double value = getStateDoubleValue(STATE_POSITION);
Double min = getMin();
Double max = getMax();
Double step = getStep();
if (value != null && max != null && min != null && step != null && min >= 0 && max >= 0 && max > min) {
if ((IncreaseDecreaseType) command == IncreaseDecreaseType.INCREASE) {
value += step;
if (value > max) {
value = max;
}
} else {
value -= step;
if (value < min) {
value = min;
}
}
sendAction(value.toString());
}
}
}

private PercentType getChannelState() {
Double value = mapLoxoneToOH(getStateDoubleValue(STATE_POSITION));
if (value != null && value >= 0 && value <= 100) {
return new PercentType(value.intValue());
}
return null;
}

/**
* Sets the current position of the dimmer
*
* @param position position to move to (0-100, 0 - full off, 100 - full on)
* @throws IOException error communicating with the Miniserver
*/
private void setPosition(Double position) throws IOException {
Double loxonePosition = mapOHToLoxone(position);
if (loxonePosition != null) {
sendAction(loxonePosition.toString());
}
}

private Double mapLoxoneToOH(Double loxoneValue) {
if (loxoneValue != null) {
// 0 means turn dimmer off, any value above zero should be mapped from min-max range
if (Double.compare(loxoneValue, 0.0) == 0) {
return 0.0;
}
Double max = getMax();
Double min = getMin();
if (max != null && min != null && max > min && min >= 0 && max >= 0) {
return 100 * (loxoneValue - min) / (max - min);
}
}
return null;
}

private Double mapOHToLoxone(Double ohValue) {
if (ohValue != null) {
// 0 means turn dimmer off, any value above zero should be mapped to min-max range
if (Double.compare(ohValue, 0.0) == 0) {
return 0.0;
}
Double max = getMax();
Double min = getMin();
if (max != null && min != null) {
double value = min + ohValue * (max - min) / 100;
return value; // no rounding to integer value is needed as loxone is accepting floating point values
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class LxControlFactory {
add(new LxControlAlarm.Factory());
add(new LxControlColorPickerV2.Factory());
add(new LxControlDimmer.Factory());
add(new LxControlEIBDimmer.Factory());
add(new LxControlInfoOnlyAnalog.Factory());
add(new LxControlInfoOnlyDigital.Factory());
add(new LxControlIRoomControllerV2.Factory());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
import org.openhab.core.types.State;
import org.openhab.core.types.StateDescriptionFragment;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An Intelligent Room Controller V2.
Expand Down Expand Up @@ -73,8 +71,6 @@ String getType() {
private static final String CMD_SET_ABSENT_MAX_TEMPERATURE = "setAbsentMaxTemperature/";
private static final String CMD_SET_MANUAL_TEMPERATURE = "setManualTemperature/";

private final Logger logger = LoggerFactory.getLogger(LxControlIRoomControllerV2.class);

private LxControlIRoomControllerV2(LxUuid uuid) {
super(uuid);
}
Expand Down
Loading

0 comments on commit 60efeab

Please sign in to comment.