Skip to content

Commit

Permalink
Added support for HAN-FUN outlets / simple on-off devices (#10760)
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Weitkamp <[email protected]>
  • Loading branch information
cweitkamp authored May 29, 2021
1 parent 572fd3d commit 04507d9
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 82 deletions.
3 changes: 3 additions & 0 deletions bundles/org.openhab.binding.avmfritz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ 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`)
- [SmartHome Zwischenstecker innen](https://www.smarthome.de/geraete/smarthome-zwischenstecker-innen-weiss) - a switchable indoor outlet (thing type `HAN_FUN_ON_OFF`)
- [SmartHome Zwischenstecker außen](https://www.smarthome.de/geraete/smarthome-zwischenstecker-aussen-schwarz) - a switchable outdoor outlet (thing type `HAN_FUN_ON_OFF`)
- [Rollotron DECT 1213](https://www.rademacher.de/shop/rollladen-sonnenschutz/elektrischer-gurtwickler/rollotron-dect-1213) - an electronic belt winder (thing type `HAN_FUN_BLINDS`)
- [Becker BoxCTRL](https://becker-antriebe.shop/) - a radio controlled roller shutter drive (thing type `HAN_FUN_BLINDS`)

Expand Down Expand Up @@ -176,6 +178,7 @@ The AIN (actor identification number) can be found in the FRITZ!Box interface ->
| power | Number:Power | Current power consumption | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!Powerline 546E |
| voltage | Number:ElectricPotential | Current voltage - FRITZ!OS 7 | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!Powerline 546E |
| outlet | Switch | Switchable outlet (ON/OFF) | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!Powerline 546E |
| on_off | Switch | Switchable device (ON/OFF) | HAN_FUN_ON_OFF |
| actual_temp | Number:Temperature | Current temperature of heating thermostat | FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT |
| set_temp | Number:Temperature | Set Temperature of heating thermostat | FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT |
| eco_temp | Number:Temperature | Eco Temperature of heating thermostat | FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,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_ON_OFF = "HAN_FUN_ON_OFF";
public static final String DEVICE_HAN_FUN_BLINDS = "HAN_FUN_BLINDS";

// List of main group types
Expand All @@ -74,6 +75,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_ON_OFF_THING_TYPE = new ThingTypeUID(BINDING_ID, DEVICE_HAN_FUN_ON_OFF);
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);
Expand Down Expand Up @@ -129,6 +131,7 @@ public class AVMFritzBindingConstants {
public static final String CHANNEL_PRESS = "press";
public static final String CHANNEL_LAST_CHANGE = "last_change";
public static final String CHANNEL_ROLLERSHUTTER = "rollershutter";
public static final String CHANNEL_ON_OFF = "on_off";

// List of all Channel config ids
public static final String CONFIG_CHANNEL_TEMP_OFFSET = "offset";
Expand Down Expand Up @@ -169,7 +172,7 @@ public class AVMFritzBindingConstants {

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,
HAN_FUN_BLINDS_THING_TYPE);
HAN_FUN_ON_OFF_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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;

import org.eclipse.jdt.annotation.Nullable;

/**
* See {@link DeviceListModel}.
*
Expand All @@ -34,6 +36,8 @@
* <li>Bit 10: AVM DECT Repeater</li>
* <li>Bit 11: Mikrofon</li>
* <li>Bit 13: HAN-FUN Unit</li>
* <li>Bit 15: an-/ausschaltbares Gerät / Steckdose / Lampe / Aktor</li>
* <li>Bit 18: Rollladen - hoch, runter, stop und level 0% bis 100 %</li>
* </ol>
*
* @author Robert Bausdorf - Initial contribution
Expand All @@ -53,6 +57,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_ON_OFF_BIT = 1 << 15; // Bit 15
protected static final int HAN_FUN_BLINDS_BIT = 1 << 18; // Bit 18
protected static final int HUMIDITY_SENSOR_BIT = 1 << 20; // Bit 20 - undocumented

Expand Down Expand Up @@ -89,12 +94,19 @@ public abstract class AVMFritzBaseModel implements BatteryModel {
@XmlElement(name = "switch")
private SwitchModel switchModel;

@XmlElement(name = "simpleonoff")
private @Nullable SimpleOnOffModel simpleOnOffUnit;

@XmlElement(name = "powermeter")
private PowerMeterModel powermeterModel;

@XmlElement(name = "hkr")
private HeatingModel heatingModel;

public @Nullable SimpleOnOffModel getSimpleOnOffUnit() {
return simpleOnOffUnit;
}

public PowerMeterModel getPowermeter() {
return powermeterModel;
}
Expand Down Expand Up @@ -151,7 +163,7 @@ public boolean isSwitchableOutlet() {
return (bitmask & OUTLET_BIT) > 0;
}

public boolean isTempSensor() {
public boolean isTemperatureSensor() {
return (bitmask & TEMPERATURE_SENSOR_BIT) > 0;
}

Expand All @@ -171,14 +183,18 @@ public boolean isHeatingThermostat() {
return (bitmask & HEATING_THERMOSTAT_BIT) > 0;
}

public boolean isMicrophone() {
public boolean hasMicrophone() {
return (bitmask & MICROPHONE_BIT) > 0;
}

public boolean isHANFUNUnit() {
return (bitmask & HAN_FUN_UNIT_BIT) > 0;
}

public boolean isHANFUNOnOff() {
return (bitmask / HAN_FUN_ON_OFF_BIT) > 0;
}

public boolean isHANFUNBlinds() {
return (bitmask & HAN_FUN_BLINDS_BIT) > 0;
}
Expand Down Expand Up @@ -215,19 +231,19 @@ public BigDecimal getBatterylow() {

@Override
public String toString() {
return new StringBuilder().append("[ain=").append(ident).append(",bitmask=").append(bitmask)
.append(",isHANFUNDevice=").append(isHANFUNDevice()).append(",isHANFUNButton=").append(isHANFUNButton())
return new StringBuilder("[ain=").append(ident).append(",bitmask=").append(bitmask).append(",isHANFUNDevice=")
.append(isHANFUNDevice()).append(",isHANFUNButton=").append(isHANFUNButton())
.append(",isHANFUNAlarmSensor=").append(isHANFUNAlarmSensor()).append(",isButton=").append(isButton())
.append(",isSwitchableOutlet=").append(isSwitchableOutlet()).append(",isTempSensor=")
.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(",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();
.append(",isSwitchableOutlet=").append(isSwitchableOutlet()).append(",isTemperatureSensor=")
.append(isTemperatureSensor()).append(",isHumiditySensor=").append(isHumiditySensor())
.append(",isPowermeter=").append(isPowermeter()).append(",isDectRepeater=").append(isDectRepeater())
.append(",isHeatingThermostat=").append(isHeatingThermostat()).append(",hasMicrophone=")
.append(hasMicrophone()).append(",isHANFUNUnit=").append(isHANFUNUnit()).append(",isHANFUNOnOff=")
.append(isHANFUNOnOff()).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(getSimpleOnOffUnit())
.append(",").append(getPowermeter()).append(",").append(getHkr()).append(",").toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,25 +90,30 @@ public void setEtsiunitinfo(ETSUnitInfoModel etsiunitinfo) {

@Override
public String toString() {
return new StringBuilder().append(super.toString()).append(temperature).append(",").append(humidity).append(",")
return new StringBuilder(super.toString()).append(temperature).append(",").append(humidity).append(",")
.append(alert).append(",").append(getButtons()).append(",").append(etsiunitinfo).append("]").toString();
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "etsideviceid", "unittype", "interfaces" })
public static class ETSUnitInfoModel {
public static final String HAN_FUN_UNITTYPE_AC_OUTLET = "262";
public static final String HAN_FUN_UNITTYPE_AC_OUTLET_SIMPLE_POWER_METERING = "263";
public static final String HAN_FUN_UNITTYPE_SIMPLE_BUTTON = "273";
public static final String HAN_FUN_UNITTYPE_SIMPLE_DETECTOR = "512";
public static final String HAN_FUN_UNITTYPE_MAGNETIC_CONTACT = "513";
public static final String HAN_FUN_UNITTYPE_OPTICAL_CONTACT = "514";
public static final String HAN_FUN_UNITTYPE_DOOR_OPEN_CLOSE_DETECTOR = "513";
public static final String HAN_FUN_UNITTYPE_WINDOW_OPEN_CLOSE_DETECTOR = "514";
public static final String HAN_FUN_UNITTYPE_MOTION_DETECTOR = "515";
public static final String HAN_FUN_UNITTYPE_SMOKE_DETECTOR = "516";
public static final String HAN_FUN_UNITTYPE_SMOKE_DETECTOR = "516"; // undocumented
public static final String HAN_FUN_UNITTYPE_FLOOD_DETECTOR = "518";
public static final String HAN_FUN_UNITTYPE_GLAS_BREAK_DETECTOR = "519";
public static final String HAN_FUN_UNITTYPE_VIBRATION_DETECTOR = "520";
public static final String HAN_FUN_UNITTYPE_SIREN = "640";

public static final String HAN_FUN_INTERFACE_ALERT = "256";
public static final String HAN_FUN_INTERFACE_KEEP_ALIVE = "277";
public static final String HAN_FUN_INTERFACE_ON_OFF = "512";
public static final String HAN_FUN_INTERFACE_SIMPLE_POWER_METERING = "768"; // undocumented
public static final String HAN_FUN_INTERFACE_SIMPLE_BUTTON = "772";

private String etsideviceid;
Expand Down Expand Up @@ -141,8 +146,8 @@ public void setInterfaces(String interfaces) {

@Override
public String toString() {
return new StringBuilder().append("[etsideviceid=").append(etsideviceid).append(",unittype=")
.append(unittype).append(",interfaces=").append(interfaces).append("]").toString();
return new StringBuilder("[etsideviceid=").append(etsideviceid).append(",unittype=").append(unittype)
.append(",interfaces=").append(interfaces).append("]").toString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 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 javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;

/**
* See {@link DeviceListModel}.
*
* @author Joshua Bacher - Initial contribution
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "state" })
public class SimpleOnOffModel {

public boolean state;

@Override
public String toString() {
return new StringBuilder("[state=").append(state).append(']').toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ public String getThingTypeId(AVMFritzBaseModel device) {
return DEVICE_HAN_FUN_CONTACT;
} else if (interfaces.contains(HAN_FUN_INTERFACE_SIMPLE_BUTTON)) {
return DEVICE_HAN_FUN_SWITCH;
} else if (interfaces.contains(HAN_FUN_INTERFACE_ON_OFF)) {
return DEVICE_HAN_FUN_ON_OFF;
}
}
return device.getProductName().replaceAll(INVALID_PATTERN, "_");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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.SimpleOnOffModel;
import org.openhab.binding.avmfritz.internal.dto.SwitchModel;
import org.openhab.binding.avmfritz.internal.dto.TemperatureModel;
import org.openhab.binding.avmfritz.internal.hardware.FritzAhaStatusListener;
Expand Down Expand Up @@ -140,9 +141,12 @@ public void onDeviceUpdated(ThingUID thingUID, AVMFritzBaseModel device) {
if (device.isHeatingThermostat()) {
updateHeatingThermostat(device.getHkr());
}
if (device.isHANFUNUnit() && device.isHANFUNOnOff()) {
updateSimpleOnOffUnit(device.getSimpleOnOffUnit());
}
if (device instanceof DeviceModel) {
DeviceModel deviceModel = (DeviceModel) device;
if (deviceModel.isTempSensor()) {
if (deviceModel.isTemperatureSensor()) {
updateTemperatureSensor(deviceModel.getTemperature());
}
if (deviceModel.isHumiditySensor()) {
Expand Down Expand Up @@ -225,8 +229,13 @@ protected void updateBattery(BatteryModel batteryModel) {
if (lowBattery == null) {
updateThingChannelState(CHANNEL_BATTERY_LOW, UnDefType.UNDEF);
} else {
updateThingChannelState(CHANNEL_BATTERY_LOW,
BatteryModel.BATTERY_ON.equals(lowBattery) ? OnOffType.ON : OnOffType.OFF);
updateThingChannelState(CHANNEL_BATTERY_LOW, OnOffType.from(BatteryModel.BATTERY_ON.equals(lowBattery)));
}
}

private void updateSimpleOnOffUnit(@Nullable SimpleOnOffModel simpleOnOffUnit) {
if (simpleOnOffUnit != null) {
updateThingChannelState(CHANNEL_ON_OFF, OnOffType.from(simpleOnOffUnit.state));
}
}

Expand All @@ -241,7 +250,7 @@ private void updateSwitchableOutlet(@Nullable SwitchModel switchModel) {
if (state == null) {
updateThingChannelState(CHANNEL_OUTLET, UnDefType.UNDEF);
} else {
updateThingChannelState(CHANNEL_OUTLET, SwitchModel.ON.equals(state) ? OnOffType.ON : OnOffType.OFF);
updateThingChannelState(CHANNEL_OUTLET, OnOffType.from(SwitchModel.ON.equals(state)));
}
}
}
Expand Down Expand Up @@ -370,11 +379,9 @@ public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Channel {} is a read-only channel and cannot handle command '{}'", channelId, command);
break;
case CHANNEL_OUTLET:
case CHANNEL_ON_OFF:
if (command instanceof OnOffType) {
fritzBox.setSwitch(ain, OnOffType.ON.equals(command));
if (state != null) {
state.getSwitch().setState(OnOffType.ON.equals(command) ? SwitchModel.ON : SwitchModel.OFF);
}
}
break;
case CHANNEL_SETTEMP:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ public class Powerline546EHandler extends AVMFritzBaseBridgeHandler implements F

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

/**
* keeps track of the current state for handling of increase/decrease
*/
private @Nullable AVMFritzBaseModel state;
private @Nullable String identifier;

/**
Expand Down Expand Up @@ -128,7 +124,6 @@ public void onDeviceUpdated(ThingUID thingUID, AVMFritzBaseModel device) {
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Device not present");
}
state = device;

updateProperties(device);

Expand Down Expand Up @@ -272,11 +267,8 @@ public void handleCommand(ChannelUID channelUID, Command command) {
}
break;
case CHANNEL_OUTLET:
fritzBox.setSwitch(ain, OnOffType.ON.equals(command));
if (command instanceof OnOffType) {
if (state != null) {
state.getSwitch().setState(OnOffType.ON.equals(command) ? SwitchModel.ON : SwitchModel.OFF);
}
fritzBox.setSwitch(ain, OnOffType.ON.equals(command));
}
break;
default:
Expand Down
Loading

0 comments on commit 04507d9

Please sign in to comment.