From 18989d7ab7c7f5b84a84d0f902e2683374d0292c Mon Sep 17 00:00:00 2001 From: quidam Date: Thu, 15 Apr 2021 17:50:11 +0200 Subject: [PATCH] [avmfritz] Added support for HAN-FUN blinds (#10492) * Added support for HAN-FUN blinds Closes #10430 Signed-off-by: Ulrich Mertin --- .../org.openhab.binding.avmfritz/README.md | 2 + .../internal/AVMFritzBindingConstants.java | 7 +- .../internal/dto/AVMFritzBaseModel.java | 19 +++-- .../avmfritz/internal/dto/DeviceModel.java | 10 +++ .../internal/dto/LevelcontrolModel.java | 58 ++++++++++++++ .../handler/AVMFritzBaseBridgeHandler.java | 4 + .../handler/AVMFritzBaseThingHandler.java | 38 +++++++++ .../hardware/FritzAhaWebInterface.java | 14 ++++ .../FritzAhaSetBlindLevelCallback.java | 56 +++++++++++++ .../FritzAhaSetBlindTargetCallback.java | 69 ++++++++++++++++ .../OH-INF/i18n/avmfritz_de.properties | 6 ++ .../resources/OH-INF/thing/channel-types.xml | 8 ++ .../resources/OH-INF/thing/thing-types.xml | 18 +++++ .../dto/AVMFritzDeviceListModelTest.java | 79 ++++++++++++++++++- .../AVMFritzDiscoveryServiceOSGiTest.java | 57 ++++++++++++- 15 files changed, 436 insertions(+), 9 deletions(-) create mode 100644 bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/LevelcontrolModel.java create mode 100644 bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindLevelCallback.java create mode 100644 bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindTargetCallback.java diff --git a/bundles/org.openhab.binding.avmfritz/README.md b/bundles/org.openhab.binding.avmfritz/README.md index 68dda5499..d5f7df539 100644 --- a/bundles/org.openhab.binding.avmfritz/README.md +++ b/bundles/org.openhab.binding.avmfritz/README.md @@ -84,6 +84,7 @@ The following sensors have been successfully tested using FRITZ!OS 7 for FRITZ!B - [SmartHome Bewegungsmelder](https://www.smarthome.de/geraete/telekom-smarthome-bewegungsmelder-innen) - a motion sensor (thing type `HAN_FUN_CONTACT`) - [SmartHome Rauchmelder](https://www.smarthome.de/geraete/smarthome-rauchmelder-weiss) - a smoke detector (thing type `HAN_FUN_CONTACT`) - [SmartHome Wandtaster](https://www.smarthome.de/geraete/telekom-smarthome-wandtaster) - a switch with two buttons (thing type `HAN_FUN_SWITCH`) +- [Rollershutter/Blinds](https://www.rademacher.de/shop/rollladen-sonnenschutz/elektrischer-gurtwickler/rollotron-dect-1213) - an electronic belt winder (thing type `HAN_FUN_BLINDS`) The use of other Sensors should be possible, if these are compatible with DECT-ULE / HAN-FUN standards. @@ -185,6 +186,7 @@ The AIN (actor identification number) can be found in the FRITZ!Box interface -> | battery_low | Switch | Battery level low (ON/OFF) - FRITZ!OS 6.80 | FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT, FRITZ!DECT 400, FRITZ!DECT 440 | | contact_state | Contact | Contact state information (OPEN/CLOSED). | HAN-FUN contact (e.g. SmartHome Tür-/Fensterkontakt or SmartHome Bewegungsmelder)- FRITZ!OS 7 | | last_change | DateTime | States the last time the button was pressed. | FRITZ!DECT 400, FRITZ!DECT 440, HAN-FUN switch (e.g. SmartHome Wandtaster) - FRITZ!OS 7 | +| rollershutter | Rollershutter | Rollershutter control and status. Accepts UP/DOWN/STOP commands and the opening level in percent. States the opening level in percent. | HAN-FUN blind (e.g. Rolltron DECT 1213) - FRITZ!OS 7 | ### Triggers diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/AVMFritzBindingConstants.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/AVMFritzBindingConstants.java index 05b60fc34..792e7e466 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/AVMFritzBindingConstants.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/AVMFritzBindingConstants.java @@ -28,6 +28,7 @@ import org.openhab.core.thing.ThingTypeUID; * @author Christoph Weitkamp - Added support for AVM FRITZ!DECT 300 and Comet DECT * @author Christoph Weitkamp - Added support for groups * @author Christoph Weitkamp - Added channels 'voltage' and 'battery_level' + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @NonNullByDefault public class AVMFritzBindingConstants { @@ -52,6 +53,7 @@ public class AVMFritzBindingConstants { public static final String DEVICE_COMETDECT = "Comet_DECT"; public static final String DEVICE_HAN_FUN_CONTACT = "HAN_FUN_CONTACT"; public static final String DEVICE_HAN_FUN_SWITCH = "HAN_FUN_SWITCH"; + public static final String DEVICE_HAN_FUN_BLINDS = "HAN_FUN_BLINDS"; // List of main group types public static final String GROUP_HEATING = "FRITZ_GROUP_HEATING"; @@ -72,6 +74,7 @@ public class AVMFritzBindingConstants { public static final ThingTypeUID COMETDECT_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_COMETDECT); public static final ThingTypeUID HAN_FUN_CONTACT_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_HAN_FUN_CONTACT); public static final ThingTypeUID HAN_FUN_SWITCH_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_HAN_FUN_SWITCH); + public static final ThingTypeUID HAN_FUN_BLINDS_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_HAN_FUN_BLINDS); public static final ThingTypeUID GROUP_HEATING_THING_TYPE = new ThingTypeUID(BINDING_ID, GROUP_HEATING); public static final ThingTypeUID GROUP_SWITCH_THING_TYPE = new ThingTypeUID(BINDING_ID, GROUP_SWITCH); @@ -125,6 +128,7 @@ public class AVMFritzBindingConstants { public static final String CHANNEL_CONTACT_STATE = "contact_state"; public static final String CHANNEL_PRESS = "press"; public static final String CHANNEL_LAST_CHANGE = "last_change"; + public static final String CHANNEL_ROLLERSHUTTER = "rollershutter"; // List of all Channel config ids public static final String CONFIG_CHANNEL_TEMP_OFFSET = "offset"; @@ -164,7 +168,8 @@ public class AVMFritzBindingConstants { COMETDECT_THING_TYPE); public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = Set.of(DECT100_THING_TYPE, - DECT200_THING_TYPE, DECT210_THING_TYPE, PL546E_THING_TYPE, HAN_FUN_CONTACT_THING_TYPE); + DECT200_THING_TYPE, DECT210_THING_TYPE, PL546E_THING_TYPE, HAN_FUN_CONTACT_THING_TYPE, + HAN_FUN_BLINDS_THING_TYPE); public static final Set SUPPORTED_GROUP_THING_TYPES_UIDS = Set.of(GROUP_HEATING_THING_TYPE, GROUP_SWITCH_THING_TYPE); diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzBaseModel.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzBaseModel.java index d13e96dbc..60e38a1b2 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzBaseModel.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzBaseModel.java @@ -39,6 +39,7 @@ import javax.xml.bind.annotation.XmlElement; * @author Robert Bausdorf - Initial contribution * @author Christoph Weitkamp - Added support for AVM FRITZ!DECT 300 and Comet DECT * @author Christoph Weitkamp - Added support for groups + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ public abstract class AVMFritzBaseModel implements BatteryModel { protected static final int HAN_FUN_DEVICE_BIT = 1; // Bit 0 @@ -52,6 +53,7 @@ public abstract class AVMFritzBaseModel implements BatteryModel { protected static final int DECT_REPEATER_BIT = 1 << 10; // Bit 10 protected static final int MICROPHONE_BIT = 1 << 11; // Bit 11 protected static final int HAN_FUN_UNIT_BIT = 1 << 13; // Bit 13 + protected static final int HAN_FUN_BLINDS_BIT = 1 << 18; // Bit 18 protected static final int HUMIDITY_SENSOR_BIT = 1 << 20; // Bit 20 - undocumented @XmlAttribute(name = "identifier") @@ -177,6 +179,10 @@ public abstract class AVMFritzBaseModel implements BatteryModel { return (bitmask & HAN_FUN_UNIT_BIT) > 0; } + public boolean isHANFUNBlinds() { + return (bitmask & HAN_FUN_BLINDS_BIT) > 0; + } + public String getFirmwareVersion() { return firmwareVersion; } @@ -216,11 +222,12 @@ public abstract class AVMFritzBaseModel implements BatteryModel { .append(isTempSensor()).append(",isHumiditySensor=").append(isHumiditySensor()).append(",isPowermeter=") .append(isPowermeter()).append(",isDectRepeater=").append(isDectRepeater()) .append(",isHeatingThermostat=").append(isHeatingThermostat()).append(",isMicrophone=") - .append(isMicrophone()).append(",isHANFUNUnit=").append(isHANFUNUnit()).append(",id=").append(deviceId) - .append(",manufacturer=").append(deviceManufacturer).append(",productname=").append(productName) - .append(",fwversion=").append(firmwareVersion).append(",present=").append(present).append(",name=") - .append(name).append(",battery=").append(getBattery()).append(",batterylow=").append(getBatterylow()) - .append(",").append(getSwitch()).append(",").append(getPowermeter()).append(",").append(getHkr()) - .append(",").toString(); + .append(isMicrophone()).append(",isHANFUNUnit=").append(isHANFUNUnit()).append(",isHANFUNBlind=") + .append(isHANFUNBlinds()).append(",id=").append(deviceId).append(",manufacturer=") + .append(deviceManufacturer).append(",productname=").append(productName).append(",fwversion=") + .append(firmwareVersion).append(",present=").append(present).append(",name=").append(name) + .append(",battery=").append(getBattery()).append(",batterylow=").append(getBatterylow()).append(",") + .append(getSwitch()).append(",").append(getPowermeter()).append(",").append(getHkr()).append(",") + .toString(); } } diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/DeviceModel.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/DeviceModel.java index 9fbb401eb..9bfe1bda0 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/DeviceModel.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/DeviceModel.java @@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlType; * * @author Robert Bausdorf - Initial contribution * @author Christoph Weitkamp - Added support for groups + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "device") @@ -32,6 +33,7 @@ public class DeviceModel extends AVMFritzBaseModel { private TemperatureModel temperature; private HumidityModel humidity; private AlertModel alert; + private LevelcontrolModel levelcontrol; @XmlElement(name = "button", type = ButtonModel.class) private List buttons; @@ -62,6 +64,14 @@ public class DeviceModel extends AVMFritzBaseModel { this.alert = alertModel; } + public LevelcontrolModel getLevelcontrol() { + return levelcontrol; + } + + public void setLevelcontrol(LevelcontrolModel levelcontrol) { + this.levelcontrol = levelcontrol; + } + public List getButtons() { return buttons == null ? List.of() : buttons; } diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/LevelcontrolModel.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/LevelcontrolModel.java new file mode 100644 index 000000000..2d2198f69 --- /dev/null +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/LevelcontrolModel.java @@ -0,0 +1,58 @@ +/** + * 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.avmfritz.internal.dto; + +import java.math.BigDecimal; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * See {@link DeviceListModel}. + * + * @author Ulrich Mertin - Initial contribution + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "levelcontrol") +public class LevelcontrolModel { + + @XmlElement(name = "level") + private BigDecimal level; + + @XmlElement(name = "levelpercentage") + private BigDecimal levelPercentage; + + public BigDecimal getLevel() { + return level != null ? level : BigDecimal.ZERO; + } + + public void setLevel(BigDecimal level) { + this.level = level; + } + + public BigDecimal getLevelPercentage() { + return levelPercentage != null ? levelPercentage : BigDecimal.ZERO; + } + + public void setLevelPercentage(BigDecimal levelPercentage) { + this.levelPercentage = levelPercentage; + } + + @Override + public String toString() { + return new StringBuilder().append("[level=").append(getLevel()).append(",levelpercentage=") + .append(getLevelPercentage()).append("]").toString(); + } +} diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseBridgeHandler.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseBridgeHandler.java index 0c63ddfe3..62b3c5180 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseBridgeHandler.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseBridgeHandler.java @@ -64,6 +64,7 @@ import org.slf4j.LoggerFactory; * @author Robert Bausdorf - Initial contribution * @author Christoph Weitkamp - Added support for AVM FRITZ!DECT 300 and Comet DECT * @author Christoph Weitkamp - Added support for groups + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @NonNullByDefault public abstract class AVMFritzBaseBridgeHandler extends BaseBridgeHandler { @@ -326,6 +327,9 @@ public abstract class AVMFritzBaseBridgeHandler extends BaseBridgeHandler { return GROUP_SWITCH; } } else if (device instanceof DeviceModel && device.isHANFUNUnit()) { + if (device.isHANFUNBlinds()) { + return DEVICE_HAN_FUN_BLINDS; + } List interfaces = Arrays .asList(((DeviceModel) device).getEtsiunitinfo().getInterfaces().split(",")); if (interfaces.contains(HAN_FUN_INTERFACE_ALERT)) { diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseThingHandler.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseThingHandler.java index aed952d24..b6759a1c4 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseThingHandler.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/handler/AVMFritzBaseThingHandler.java @@ -33,19 +33,24 @@ import org.openhab.binding.avmfritz.internal.dto.DeviceModel; import org.openhab.binding.avmfritz.internal.dto.HeatingModel; import org.openhab.binding.avmfritz.internal.dto.HeatingModel.NextChangeModel; import org.openhab.binding.avmfritz.internal.dto.HumidityModel; +import org.openhab.binding.avmfritz.internal.dto.LevelcontrolModel; import org.openhab.binding.avmfritz.internal.dto.PowerMeterModel; import org.openhab.binding.avmfritz.internal.dto.SwitchModel; import org.openhab.binding.avmfritz.internal.dto.TemperatureModel; import org.openhab.binding.avmfritz.internal.hardware.FritzAhaStatusListener; import org.openhab.binding.avmfritz.internal.hardware.FritzAhaWebInterface; +import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetBlindTargetCallback.BlindCommand; import org.openhab.core.config.core.Configuration; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Bridge; @@ -73,6 +78,7 @@ import org.slf4j.LoggerFactory; * @author Robert Bausdorf - Initial contribution * @author Christoph Weitkamp - Added support for AVM FRITZ!DECT 300 and Comet DECT * @author Christoph Weitkamp - Added support for groups + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @NonNullByDefault public abstract class AVMFritzBaseThingHandler extends BaseThingHandler implements FritzAhaStatusListener { @@ -145,6 +151,9 @@ public abstract class AVMFritzBaseThingHandler extends BaseThingHandler implemen if (deviceModel.isHANFUNAlarmSensor()) { updateHANFUNAlarmSensor(deviceModel.getAlert()); } + if (deviceModel.isHANFUNBlinds()) { + updateLevelcontrol(deviceModel.getLevelcontrol()); + } } } } @@ -172,6 +181,12 @@ public abstract class AVMFritzBaseThingHandler extends BaseThingHandler implemen } } + protected void updateLevelcontrol(@Nullable LevelcontrolModel levelcontrolModel) { + if (levelcontrolModel != null) { + updateThingChannelState(CHANNEL_ROLLERSHUTTER, new PercentType(levelcontrolModel.getLevelPercentage())); + } + } + private void updateHeatingThermostat(@Nullable HeatingModel heatingModel) { if (heatingModel != null) { updateThingChannelState(CHANNEL_MODE, new StringType(heatingModel.getMode())); @@ -424,6 +439,29 @@ public abstract class AVMFritzBaseThingHandler extends BaseThingHandler implemen } } break; + case CHANNEL_ROLLERSHUTTER: + if (command instanceof StopMoveType) { + StopMoveType rollershutterCommand = (StopMoveType) command; + if (StopMoveType.STOP.equals(rollershutterCommand)) { + fritzBox.setBlind(ain, BlindCommand.STOP); + } else { + logger.debug("Received unknown rollershutter StopMove command MOVE"); + } + } else if (command instanceof UpDownType) { + UpDownType rollershutterCommand = (UpDownType) command; + if (UpDownType.UP.equals(rollershutterCommand)) { + fritzBox.setBlind(ain, BlindCommand.OPEN); + } else { + fritzBox.setBlind(ain, BlindCommand.CLOSE); + } + } else if (command instanceof PercentType) { + PercentType rollershutterCommand = (PercentType) command; + BigDecimal levelpercentage = rollershutterCommand.toBigDecimal(); + fritzBox.setLevelpercentage(ain, levelpercentage); + } else { + logger.debug("Received unknown rollershutter command type '{}'", command.toString()); + } + break; default: logger.debug("Received unknown channel {}", channelId); break; diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/FritzAhaWebInterface.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/FritzAhaWebInterface.java index 8eb2854ac..4edc01fbe 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/FritzAhaWebInterface.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/FritzAhaWebInterface.java @@ -33,6 +33,9 @@ import org.openhab.binding.avmfritz.internal.config.AVMFritzBoxConfiguration; import org.openhab.binding.avmfritz.internal.handler.AVMFritzBaseBridgeHandler; import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaApplyTemplateCallback; import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaCallback; +import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetBlindLevelCallback; +import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetBlindTargetCallback; +import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetBlindTargetCallback.BlindCommand; import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetHeatingModeCallback; import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetHeatingTemperatureCallback; import org.openhab.binding.avmfritz.internal.hardware.callbacks.FritzAhaSetSwitchCallback; @@ -49,6 +52,7 @@ import org.slf4j.LoggerFactory; * @author Christoph Weitkamp - Added support for AVM FRITZ!DECT 300 and Comet * DECT * @author Christoph Weitkamp - Added support for groups + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @NonNullByDefault public class FritzAhaWebInterface { @@ -319,4 +323,14 @@ public class FritzAhaWebInterface { FritzAhaSetHeatingModeCallback callback = new FritzAhaSetHeatingModeCallback(this, ain, command, endTime); return asyncGet(callback); } + + public FritzAhaContentExchange setLevelpercentage(String ain, BigDecimal levelpercentage) { + FritzAhaSetBlindLevelCallback callback = new FritzAhaSetBlindLevelCallback(this, ain, levelpercentage); + return asyncGet(callback); + } + + public FritzAhaContentExchange setBlind(String ain, BlindCommand command) { + FritzAhaSetBlindTargetCallback callback = new FritzAhaSetBlindTargetCallback(this, ain, command); + return asyncGet(callback); + } } diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindLevelCallback.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindLevelCallback.java new file mode 100644 index 000000000..430c0f26f --- /dev/null +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindLevelCallback.java @@ -0,0 +1,56 @@ +/** + * 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.avmfritz.internal.hardware.callbacks; + +import static org.eclipse.jetty.http.HttpMethod.GET; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.avmfritz.internal.hardware.FritzAhaWebInterface; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Callback implementation for updating blind commands. Supports reauthorization + * + * @author Ulrich Mertin - Initial contribution + */ +@NonNullByDefault +public class FritzAhaSetBlindLevelCallback extends FritzAhaReauthCallback { + + private final Logger logger = LoggerFactory.getLogger(FritzAhaSetBlindLevelCallback.class); + + private final String ain; + + /** + * Constructor + * + * @param webIface Interface to FRITZ!Box + * @param ain AIN of the device that should be switched + * @param level Opening level percentage (0 ... 100) + */ + public FritzAhaSetBlindLevelCallback(FritzAhaWebInterface webIface, String ain, BigDecimal levelpercentage) { + super(WEBSERVICE_PATH, "switchcmd=setlevelpercentage&level=" + levelpercentage + "&ain=" + ain, webIface, GET, + 1); + this.ain = ain; + } + + @Override + public void execute(int status, String response) { + super.execute(status, response); + if (isValidRequest()) { + logger.debug("Received response '{}' for item '{}'", response, ain); + } + } +} diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindTargetCallback.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindTargetCallback.java new file mode 100644 index 000000000..37a55cdfb --- /dev/null +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/hardware/callbacks/FritzAhaSetBlindTargetCallback.java @@ -0,0 +1,69 @@ +/** + * 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.avmfritz.internal.hardware.callbacks; + +import static org.eclipse.jetty.http.HttpMethod.GET; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.avmfritz.internal.hardware.FritzAhaWebInterface; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Callback implementation for updating blind commands. Supports reauthorization + * + * @author Ulrich Mertin - Initial contribution + */ +@NonNullByDefault +public class FritzAhaSetBlindTargetCallback extends FritzAhaReauthCallback { + + private final Logger logger = LoggerFactory.getLogger(FritzAhaSetBlindTargetCallback.class); + + private final String ain; + + /** + * Constructor + * + * @param webIface Interface to FRITZ!Box + * @param ain AIN of the device that should be switched + * @param command Blind command to send + */ + public FritzAhaSetBlindTargetCallback(FritzAhaWebInterface webIface, String ain, BlindCommand command) { + super(WEBSERVICE_PATH, "switchcmd=setblind&target=" + command.getTarget() + "&ain=" + ain, webIface, GET, 1); + this.ain = ain; + } + + @Override + public void execute(int status, String response) { + super.execute(status, response); + if (isValidRequest()) { + logger.debug("Received response '{}' for item '{}'", response, ain); + } + } + + public enum BlindCommand { + OPEN("open"), + CLOSE("close"), + STOP("stop"); + + private final String target; + + private BlindCommand(final String target) { + this.target = target; + } + + public String getTarget() { + return target; + } + } +} diff --git a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz_de.properties b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz_de.properties index d8728b93c..b1543faac 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz_de.properties +++ b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz_de.properties @@ -99,6 +99,9 @@ thing-type.avmfritz.FRITZ_DECT_440.description = FRITZ!DECT 440 Taster. Dient zu thing-type.avmfritz.FRITZ_Powerline_546E.description = FRITZ!Powerline 546E schaltbare Steckdose. Dient zur Steuerung der integrierten Steckdose und liefert Daten wie z.B. Temperatur. +thing-type.avmfritz.HAN_FUN_BLINDS.label = HAN-FUN Rolladen +thing-type.avmfritz.HAN_FUN_BLINDS.description = HAN-FUN Rolladen (z.B. Rollotron DECT 1213). + # thing types config groups thing-type.avmfritz.FRITZ_GROUP_HEATING.label = Heizkörperregler thing-type.avmfritz.FRITZ_GROUP_HEATING.description = Gruppe für Heizkörperregler. Dient zur Steuerung von Heizkörpern und liefert Daten wie z.B. Temperatur. @@ -214,6 +217,9 @@ thing-type.avmfritz.FRITZ_DECT_400.channel.press.description = Wird ausgel thing-type.avmfritz.FRITZ_DECT_440.channel.press.label = Tastendruck thing-type.avmfritz.FRITZ_DECT_440.channel.press.description = Wird ausgelöst, wenn eine Taste gedrückt wird. +channel-type.avmfritz.rollershutter.label = Rolladensteuerung +channel-type.avmfritz.rollershutter.description = Steuert den Rolladen und zeigt seinen Öffnungsgrad in Prozent an. + channel-type.avmfritz.last_change.label = Letzte Änderung channel-type.avmfritz.last_change.description = Zeigt an, wann der Schalter zuletzt gedrückt wurde. channel-type.avmfritz.last_change.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS diff --git a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/channel-types.xml index 87c6d3891..9c64ede17 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/channel-types.xml +++ b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/channel-types.xml @@ -231,4 +231,12 @@ Time + + + Rollershutter + + Controls the rollershutter and states its opening level in percent + Blinds + + diff --git a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/thing-types.xml index 3921f4948..bdf606ed2 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/thing/thing-types.xml @@ -277,6 +277,24 @@ + + + + + + + + HAN-FUN blinds (e.g. RolloTron DECT 1213) + + + + + + ain + + + + diff --git a/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzDeviceListModelTest.java b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzDeviceListModelTest.java index a6a3aca56..3606d52ef 100644 --- a/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzDeviceListModelTest.java +++ b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/AVMFritzDeviceListModelTest.java @@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory; * Tests for {@link DeviceListModel}. * * @author Christoph Weitkamp - Initial contribution + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @NonNullByDefault public class AVMFritzDeviceListModelTest { @@ -59,6 +60,7 @@ public class AVMFritzDeviceListModelTest { "0HAN-FUN #2: Unit #2412273772" + "1FRITZ!DECT 400 #141000" + "1FRITZ!DECT 440 #152300431000" + + "10Rollotron 1213 #11manuell2610406281256,513,516,5170" + ""; //@formatter:off try { @@ -72,7 +74,7 @@ public class AVMFritzDeviceListModelTest { @Test public void validateDeviceListModel() { assertNotNull(devices); - assertEquals(13, devices.getDevicelist().size()); + assertEquals(14, devices.getDevicelist().size()); assertEquals("1", devices.getXmlApiVersion()); } @@ -101,6 +103,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNull(device.getSwitch()); @@ -111,6 +114,8 @@ public class AVMFritzDeviceListModelTest { assertNull(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -138,6 +143,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertTrue(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNotNull(device.getSwitch()); assertEquals(SwitchModel.ON, device.getSwitch().getState()); @@ -152,6 +158,8 @@ public class AVMFritzDeviceListModelTest { validatePowerMeter(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -179,6 +187,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertTrue(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNotNull(device.getSwitch()); assertEquals(SwitchModel.ON, device.getSwitch().getState()); @@ -193,6 +202,8 @@ public class AVMFritzDeviceListModelTest { validatePowerMeter(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -220,6 +231,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertTrue(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNull(device.getSwitch()); @@ -257,6 +269,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertTrue(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNull(device.getSwitch()); @@ -294,6 +307,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertTrue(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNull(device.getSwitch()); @@ -331,6 +345,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertEquals(new BigDecimal("100"), device.getBattery()); assertEquals(BatteryModel.BATTERY_OFF, device.getBatterylow()); @@ -350,6 +365,8 @@ public class AVMFritzDeviceListModelTest { assertNull(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -377,6 +394,7 @@ public class AVMFritzDeviceListModelTest { assertTrue(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertEquals(new BigDecimal("100"), device.getBattery()); assertEquals(BatteryModel.BATTERY_OFF, device.getBatterylow()); @@ -417,6 +435,8 @@ public class AVMFritzDeviceListModelTest { assertNull(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -444,6 +464,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertTrue(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertNotNull(device.getSwitch()); assertEquals(SwitchModel.OFF, device.getSwitch().getState()); @@ -456,6 +477,8 @@ public class AVMFritzDeviceListModelTest { validatePowerMeter(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -483,6 +506,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertTrue(device.getButtons().isEmpty()); @@ -496,6 +520,8 @@ public class AVMFritzDeviceListModelTest { assertNull(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); } @Test @@ -523,6 +549,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(device.isHumiditySensor()); assertFalse(device.isPowermeter()); assertFalse(device.isHeatingThermostat()); + assertFalse(device.isHANFUNBlinds()); assertEquals(1, device.getButtons().size()); assertEquals(1529590797, device.getButtons().get(0).getLastpressedtimestamp()); @@ -536,6 +563,54 @@ public class AVMFritzDeviceListModelTest { assertNull(device.getPowermeter()); assertNull(device.getHkr()); + + assertNull(device.getLevelcontrol()); + } + + @Test + public void validateHANFUNBlindModel() { + Optional optionalDevice = findModelByIdentifier("142760503450-1"); + assertTrue(optionalDevice.isPresent()); + assertTrue(optionalDevice.get() instanceof DeviceModel); + + DeviceModel device = (DeviceModel) optionalDevice.get(); + assertEquals("Rollotron 1213", device.getProductName()); + assertEquals("142760503450-1", device.getIdentifier()); + assertEquals("2000", device.getDeviceId()); + assertEquals("0.0", device.getFirmwareVersion()); + assertEquals("0x37c4", device.getManufacturer()); + + assertEquals(1, device.getPresent()); + assertEquals("Rollotron 1213 #1", device.getName()); + + assertFalse(device.isButton()); + assertFalse(device.isHANFUNButton()); + assertTrue(device.isHANFUNAlarmSensor()); + assertFalse(device.isDectRepeater()); + assertFalse(device.isSwitchableOutlet()); + assertFalse(device.isTempSensor()); + assertFalse(device.isHumiditySensor()); + assertFalse(device.isPowermeter()); + assertFalse(device.isHeatingThermostat()); + assertTrue(device.isHANFUNBlinds()); + + assertTrue(device.getButtons().isEmpty()); + + assertNotNull(device.getAlert()); + assertEquals(BigDecimal.ZERO, device.getAlert().getState()); + + assertNull(device.getSwitch()); + + assertNull(device.getTemperature()); + + assertNull(device.getPowermeter()); + + assertNull(device.getHkr()); + + LevelcontrolModel levelcontrol = device.getLevelcontrol(); + assertNotNull(levelcontrol); + assertEquals(BigDecimal.valueOf(26L), levelcontrol.getLevel()); + assertEquals(BigDecimal.valueOf(10L), levelcontrol.getLevelPercentage()); } @Test @@ -563,6 +638,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(group.isHumiditySensor()); assertFalse(group.isPowermeter()); assertTrue(group.isHeatingThermostat()); + assertFalse(group.isHANFUNBlinds()); assertNull(group.getSwitch()); @@ -600,6 +676,7 @@ public class AVMFritzDeviceListModelTest { assertFalse(group.isHumiditySensor()); assertTrue(group.isPowermeter()); assertFalse(group.isHeatingThermostat()); + assertFalse(group.isHANFUNBlinds()); assertNotNull(group.getSwitch()); assertEquals(SwitchModel.ON, group.getSwitch().getState()); diff --git a/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceOSGiTest.java b/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceOSGiTest.java index 53bb026f2..c5e5fd5d1 100644 --- a/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceOSGiTest.java +++ b/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceOSGiTest.java @@ -43,6 +43,7 @@ import org.openhab.core.thing.ThingUID; * Tests for {@link AVMFritzDiscoveryService}. * * @author Christoph Weitkamp - Initial contribution + * @author Ulrich Mertin - Added support for HAN-FUN blinds */ @NonNullByDefault public class AVMFritzDiscoveryServiceOSGiTest extends AVMFritzThingHandlerOSGiTest { @@ -86,7 +87,7 @@ public class AVMFritzDiscoveryServiceOSGiTest extends AVMFritzThingHandlerOSGiTe @Test public void correctSupportedTypes() { - assertEquals(13, discovery.getSupportedThingTypes().size()); + assertEquals(14, discovery.getSupportedThingTypes().size()); assertTrue(discovery.getSupportedThingTypes().contains(DECT100_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(DECT200_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(DECT210_THING_TYPE)); @@ -98,6 +99,7 @@ public class AVMFritzDiscoveryServiceOSGiTest extends AVMFritzThingHandlerOSGiTe assertTrue(discovery.getSupportedThingTypes().contains(COMETDECT_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_CONTACT_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_SWITCH_THING_TYPE)); + assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_BLINDS_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(GROUP_HEATING_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(GROUP_SWITCH_THING_TYPE)); } @@ -727,6 +729,59 @@ public class AVMFritzDiscoveryServiceOSGiTest extends AVMFritzThingHandlerOSGiTe assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty()); } + @Test + public void validHANFUNBlindDiscoveryResult() throws JAXBException { + //@formatter:off + String xml = + "" + + ""+ + "1"+ + "0"+ + "Rollotron 1213 #1"+ + ""+ + "1"+ + "manuell"+ + ""+ + ""+ + "26"+ + "10"+ + ""+ + ""+ + "406"+ + "281"+ + "256,513,516,517"+ + ""+ + ""+ + "0"+ + ""+ + ""+ + "" + + ""; + //@formatter:on + + Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller(); + DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml)); + assertNotNull(devices); + assertEquals(1, devices.getDevicelist().size()); + + AVMFritzBaseModel device = devices.getDevicelist().get(0); + assertNotNull(device); + + discovery.onDeviceAdded(device); + assertNotNull(discoveryResult); + + assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag()); + assertEquals(new ThingUID("avmfritz:HAN_FUN_BLINDS:1:142760503450_1"), discoveryResult.getThingUID()); + assertEquals(HAN_FUN_BLINDS_THING_TYPE, discoveryResult.getThingTypeUID()); + assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID()); + assertEquals("142760503450-1", discoveryResult.getProperties().get(CONFIG_AIN)); + assertEquals("0x37c4", discoveryResult.getProperties().get(PROPERTY_VENDOR)); + assertEquals("Rollotron 1213", discoveryResult.getProperties().get(PRODUCT_NAME)); + assertEquals("142760503450-1", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER)); + assertEquals("0.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION)); + assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty()); + } + @Test public void validHeatingGroupDiscoveryResult() throws JAXBException { //@formatter:off