[avmfritz] Added support for HAN-FUN blinds (#10492)

* Added support for HAN-FUN blinds

Closes #10430 

Signed-off-by: Ulrich Mertin <mail@ulrich-mertin.de>
This commit is contained in:
quidam 2021-04-15 17:50:11 +02:00 committed by GitHub
parent 7dad6ea973
commit 18989d7ab7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 436 additions and 9 deletions

View File

@ -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

View File

@ -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<ThingTypeUID> 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<ThingTypeUID> SUPPORTED_GROUP_THING_TYPES_UIDS = Set.of(GROUP_HEATING_THING_TYPE,
GROUP_SWITCH_THING_TYPE);

View File

@ -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();
}
}

View File

@ -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<ButtonModel> 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<ButtonModel> getButtons() {
return buttons == null ? List.of() : buttons;
}

View File

@ -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();
}
}

View File

@ -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<String> interfaces = Arrays
.asList(((DeviceModel) device).getEtsiunitinfo().getInterfaces().split(","));
if (interfaces.contains(HAN_FUN_INTERFACE_ALERT)) {

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -231,4 +231,12 @@
<category>Time</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="rollershutter">
<item-type>Rollershutter</item-type>
<label>Rollershutter Control</label>
<description>Controls the rollershutter and states its opening level in percent</description>
<category>Blinds</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -277,6 +277,24 @@
<config-description-ref uri="thing-type:avmfritz:fritzdevice"/>
</thing-type>
<thing-type id="HAN_FUN_BLINDS">
<supported-bridge-type-refs>
<bridge-type-ref id="fritzbox"/>
<bridge-type-ref id="FRITZ_Powerline_546E_Solo"/>
</supported-bridge-type-refs>
<label>HAN-FUN Blinds</label>
<description>HAN-FUN blinds (e.g. RolloTron DECT 1213)</description>
<channels>
<channel id="rollershutter" typeId="rollershutter"/>
</channels>
<representation-property>ain</representation-property>
<config-description-ref uri="thing-type:avmfritz:fritzdevice"/>
</thing-type>
<!-- Supported FRITZ! groups and features -->
<thing-type id="FRITZ_GROUP_HEATING">
<supported-bridge-type-refs>

View File

@ -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 {
"<device identifier=\"11934 0059979-1\" id=\"2001\" functionbitmask=\"8200\" fwversion=\"0.0\" manufacturer=\"0x0feb\" productname=\"HAN-FUN\"><present>0</present><name>HAN-FUN #2: Unit #2</name><etsiunitinfo><etsideviceid>412</etsideviceid><unittype>273</unittype><interfaces>772</interfaces></etsiunitinfo><button><lastpressedtimestamp>1529590797</lastpressedtimestamp></button></device>" +
"<device identifier=\"13096 0007307\" id=\"29\" functionbitmask=\"32\" fwversion=\"04.90\" manufacturer=\"AVM\" productname=\"FRITZ!DECT 400\"><present>1</present><name>FRITZ!DECT 400 #14</name><battery>100</battery><batterylow>0</batterylow><button identifier=\"13096 0007307-0\" id=\"5000\"><name>FRITZ!DECT 400 #14: kurz</name><lastpressedtimestamp>1549195586</lastpressedtimestamp></button><button identifier=\"13096 0007307-9\" id=\"5001\"><name>FRITZ!DECT 400 #14: lang</name><lastpressedtimestamp>1549195595</lastpressedtimestamp></button></device>" +
"<device identifier=\"13096 0007308\" id=\"30\" functionbitmask=\"1048864\" fwversion=\"05.10\" manufacturer=\"AVM\" productname=\"FRITZ!DECT 440\"><present>1</present><name>FRITZ!DECT 440 #15</name><temperature><celsius>230</celsius><offset>0</offset></temperature><humidity><rel_humidity>43</rel_humidity></humidity><battery>100</battery><batterylow>0</batterylow><button identifier=\"13096 0007308-1\" id=\"5000\"><name>FRITZ!DECT 440 #15: Oben rechts</name><lastpressedtimestamp>1549195586</lastpressedtimestamp></button><button identifier=\"13096 0007308-3\" id=\"5001\"><name>FRITZ!DECT 440 #15: Unten rechts</name><lastpressedtimestamp>1549195595</lastpressedtimestamp></button><button identifier=\"13096 0007308-5\" id=\"5002\"><name>FRITZ!DECT 440 #15: Unten links</name><lastpressedtimestamp>1549195586</lastpressedtimestamp></button><button identifier=\"13096 0007308-7\" id=\"5003\"><name>FRITZ!DECT 440 #15: Oben links</name><lastpressedtimestamp>1549195595</lastpressedtimestamp></button></device>" +
"<device identifier=\"14276 0503450-1\" id=\"2000\" functionbitmask=\"335888\" fwversion=\"0.0\" manufacturer=\"0x37c4\" productname=\"Rollotron 1213\"><present>1</present><txbusy>0</txbusy><name>Rollotron 1213 #1</name><blind><endpositionsset>1</endpositionsset><mode>manuell</mode></blind><levelcontrol><level>26</level><levelpercentage>10</levelpercentage></levelcontrol><etsiunitinfo><etsideviceid>406</etsideviceid><unittype>281</unittype><interfaces>256,513,516,517</interfaces></etsiunitinfo><alert><state>0</state><lastalertchgtimestamp></lastalertchgtimestamp></alert></device>" +
"</devicelist>";
//@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<AVMFritzBaseModel> 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());

View File

@ -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 =
"<devicelist version=\"1\">" +
"<device identifier=\"14276 0503450-1\" id=\"2000\" functionbitmask=\"335888\" fwversion=\"0.0\" manufacturer=\"0x37c4\" productname=\"Rollotron 1213\">"+
"<present>1</present>"+
"<txbusy>0</txbusy>"+
"<name>Rollotron 1213 #1</name>"+
"<blind>"+
"<endpositionsset>1</endpositionsset>"+
"<mode>manuell</mode>"+
"</blind>"+
"<levelcontrol>"+
"<level>26</level>"+
"<levelpercentage>10</levelpercentage>"+
"</levelcontrol>"+
"<etsiunitinfo>"+
"<etsideviceid>406</etsideviceid>"+
"<unittype>281</unittype>"+
"<interfaces>256,513,516,517</interfaces>"+
"</etsiunitinfo>"+
"<alert>"+
"<state>0</state>"+
"<lastalertchgtimestamp></lastalertchgtimestamp>"+
"</alert>"+
"</device>" +
"</devicelist>";
//@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