Skip to content

Commit

Permalink
[avmfritz] Added ThingActions to handle Boost and Window-Open modes f…
Browse files Browse the repository at this point in the history
…or heating devices / groups (#8222)

* Added ThingActions to handle Boost and Window-Open modes

Signed-off-by: Christoph Weitkamp <[email protected]>
  • Loading branch information
cweitkamp authored Sep 2, 2020
1 parent aa32b69 commit cfd2ae5
Show file tree
Hide file tree
Showing 23 changed files with 705 additions and 170 deletions.
17 changes: 15 additions & 2 deletions bundles/org.openhab.binding.avmfritz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ The FRITZ!Box has to run at least on firmware FRITZ!OS 7.

The FRITZ!OS supports two different types of groups.
On the one hand there are groups for heating thermostats on the other hand there are groups for switchable outlets and power meters.
The first one provides the same channels like the [FRITZ!DECT 301 / FRITZ!DECT 300 / Comet DECT](https://www.openhab.org/addons/bindings/avmfritz/#fritz-dect-301-fritz-dect-300-comet-dect) devices.
The later one provides the same channels like the [FRITZ!DECT 200 / FRITZ!DECT 210](https://www.openhab.org/addons/bindings/avmfritz/#fritz-dect-200-fritz-dect-210) / [FRITZ!Powerline 546E](https://www.openhab.org/addons/bindings/avmfritz/#fritz-powerline-546e) devices.
The first one provides the same channels and actions like the [FRITZ!DECT 301 / FRITZ!DECT 300 / Comet DECT](https://www.openhab.org/addons/bindings/avmfritz/#fritz-dect-301-fritz-dect-300-comet-dect) devices.
The latter provides the same channels like the [FRITZ!DECT 200 / FRITZ!DECT 210](https://www.openhab.org/addons/bindings/avmfritz/#fritz-dect-200-fritz-dect-210) / [FRITZ!Powerline 546E](https://www.openhab.org/addons/bindings/avmfritz/#fritz-powerline-546e) devices.
The FRITZ!Box has to run at least on firmware FRITZ!OS 6.69.

## Discovery
Expand Down Expand Up @@ -191,6 +191,19 @@ then
end
```

### Actions

For heating devices and heating groups there are two actions available to set Boost or Window Open mode for a given duration: `setBoostMode(long)` and `setWindowOpenMode(long)`.
The duration has to be given in seconds, min. 1, max. 86400, 0 for deactivation.


```
val actions = getActions("avmfritz","avmfritz:Comet_DECT:1:aaaaaabbbbbb")
// set Boost mode for 5 min
actions.setBoostMode(300)
```

## Full Example

demo.things:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Copyright (c) 2010-2020 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.actions;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.thing.binding.ThingActions;
import org.eclipse.smarthome.core.thing.binding.ThingActionsScope;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.openhab.binding.avmfritz.internal.actions.IAVMFritzHeatingActions;
import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingActionsHandler;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.RuleAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link AVMFritzHeatingActions} defines thing actions for heating devices / groups of the avmfritz binding.
*
* @author Christoph Weitkamp - Initial contribution
*/
@ThingActionsScope(name = "avmfritz")
@NonNullByDefault
public class AVMFritzHeatingActions implements ThingActions, IAVMFritzHeatingActions {

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

private @Nullable AVMFritzHeatingActionsHandler handler;

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
this.handler = (AVMFritzHeatingActionsHandler) handler;
}

@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}

@Override
@RuleAction(label = "@text/setBoostModeModeActionLabel", description = "@text/setBoostModeActionDescription")
public void setBoostMode(
@ActionInput(name = "Duration", label = "@text/setBoostModeDurationInputLabel", description = "@text/setBoostModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
AVMFritzHeatingActionsHandler actionsHandler = handler;
if (actionsHandler == null) {
throw new IllegalArgumentException("AVMFritzHeatingActions ThingHandler is null!");
}
if (duration == null) {
throw new IllegalArgumentException("Cannot set Boost mode as 'duration' is null!");
}
actionsHandler.setBoostMode(duration.longValue());
}

public static void setBoostMode(@Nullable ThingActions actions, @Nullable Long duration) {
invokeMethodOf(actions).setBoostMode(duration);
}

@Override
@RuleAction(label = "@text/setWindowOpenModeActionLabel", description = "@text/setWindowOpenModeActionDescription")
public void setWindowOpenMode(
@ActionInput(name = "Duration", label = "@text/setWindowOpenModeDurationInputLabel", description = "@text/setWindowOpenModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
AVMFritzHeatingActionsHandler actionsHandler = handler;
if (actionsHandler == null) {
throw new IllegalArgumentException("AVMFritzHeatingActions ThingHandler is null!");
}
if (duration == null) {
throw new IllegalArgumentException("Cannot set Window Open mode as 'duration' is null!");
}
actionsHandler.setWindowOpenMode(duration.longValue());
}

public static void setWindowOpenMode(@Nullable ThingActions actions, @Nullable Long duration) {
invokeMethodOf(actions).setWindowOpenMode(duration);
}

private static IAVMFritzHeatingActions invokeMethodOf(@Nullable ThingActions actions) {
if (actions == null) {
throw new IllegalArgumentException("actions cannot be null");
}
if (actions.getClass().getName().equals(AVMFritzHeatingActions.class.getName())) {
if (actions instanceof IAVMFritzHeatingActions) {
return (IAVMFritzHeatingActions) actions;
} else {
return (IAVMFritzHeatingActions) Proxy.newProxyInstance(IAVMFritzHeatingActions.class.getClassLoader(),
new Class[] { IAVMFritzHeatingActions.class }, (Object proxy, Method method, Object[] args) -> {
Method m = actions.getClass().getDeclaredMethod(method.getName(),
method.getParameterTypes());
return m.invoke(actions, args);
});
}
}
throw new IllegalArgumentException("Actions is not an instance of AVMFritzHeatingActions");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,12 @@ public class AVMFritzBindingConstants {
public static final Set<ThingTypeUID> SUPPORTED_BUTTON_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(DECT400_THING_TYPE, HAN_FUN_SWITCH_THING_TYPE).collect(Collectors.toSet()));

public static final Set<ThingTypeUID> SUPPORTED_HEATING_THING_TYPES = Collections.unmodifiableSet(
Stream.of(DECT300_THING_TYPE, DECT301_THING_TYPE, COMETDECT_THING_TYPE).collect(Collectors.toSet()));

public static final Set<ThingTypeUID> SUPPORTED_DEVICE_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream
.of(DECT100_THING_TYPE, DECT200_THING_TYPE, DECT210_THING_TYPE, DECT300_THING_TYPE,
DECT301_THING_TYPE, PL546E_THING_TYPE, COMETDECT_THING_TYPE, HAN_FUN_CONTACT_THING_TYPE)
.collect(Collectors.toSet()));
.unmodifiableSet(Stream.of(DECT100_THING_TYPE, DECT200_THING_TYPE, DECT210_THING_TYPE, PL546E_THING_TYPE,
HAN_FUN_CONTACT_THING_TYPE).collect(Collectors.toSet()));

public static final Set<ThingTypeUID> SUPPORTED_GROUP_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(GROUP_HEATING_THING_TYPE, GROUP_SWITCH_THING_TYPE).collect(Collectors.toSet()));
Expand All @@ -161,7 +162,7 @@ public class AVMFritzBindingConstants {
.unmodifiableSet(Stream.of(BRIDGE_THING_TYPE, PL546E_STANDALONE_THING_TYPE).collect(Collectors.toSet()));

public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream
.of(SUPPORTED_BUTTON_THING_TYPES_UIDS.stream(), SUPPORTED_DEVICE_THING_TYPES_UIDS.stream(),
SUPPORTED_GROUP_THING_TYPES_UIDS.stream(), SUPPORTED_BRIDGE_THING_TYPES_UIDS.stream())
.reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toSet()));
.of(SUPPORTED_BUTTON_THING_TYPES_UIDS, SUPPORTED_HEATING_THING_TYPES, SUPPORTED_DEVICE_THING_TYPES_UIDS,
SUPPORTED_GROUP_THING_TYPES_UIDS, SUPPORTED_BRIDGE_THING_TYPES_UIDS)
.flatMap(Set::stream).collect(Collectors.toSet()));
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
import org.eclipse.smarthome.io.net.http.HttpClientFactory;
import org.openhab.binding.avmfritz.internal.handler.AVMFritzButtonHandler;
import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingDeviceHandler;
import org.openhab.binding.avmfritz.internal.handler.AVMFritzHeatingGroupHandler;
import org.openhab.binding.avmfritz.internal.handler.BoxHandler;
import org.openhab.binding.avmfritz.internal.handler.DeviceHandler;
import org.openhab.binding.avmfritz.internal.handler.GroupHandler;
Expand Down Expand Up @@ -76,8 +78,12 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return new Powerline546EHandler((Bridge) thing, httpClient, commandDescriptionProvider);
} else if (SUPPORTED_BUTTON_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new AVMFritzButtonHandler(thing);
} else if (SUPPORTED_HEATING_THING_TYPES.contains(thingTypeUID)) {
return new AVMFritzHeatingDeviceHandler(thing);
} else if (SUPPORTED_DEVICE_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new DeviceHandler(thing);
} else if (GROUP_HEATING_THING_TYPE.equals(thingTypeUID)) {
return new AVMFritzHeatingGroupHandler(thing);
} else if (SUPPORTED_GROUP_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new GroupHandler(thing);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.actions;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.avmfritz.actions.AVMFritzHeatingActions;

/**
* The {@link IAVMFritzHeatingActions} defines the interface for all thing actions supported by the binding.
* These methods, parameters, and return types are explained in {@link AVMFritzHeatingActions}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public interface IAVMFritzHeatingActions {

void setBoostMode(@Nullable Long duration);

void setWindowOpenMode(@Nullable Long duration);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -55,10 +56,12 @@ public class AVMFritzDiscoveryService extends AbstractDiscoveryService
private @NonNullByDefault({}) AVMFritzBaseBridgeHandler bridgeHandler;

public AVMFritzDiscoveryService() {
super(Collections.unmodifiableSet(Stream
.of(SUPPORTED_BUTTON_THING_TYPES_UIDS.stream(), SUPPORTED_DEVICE_THING_TYPES_UIDS.stream(),
SUPPORTED_GROUP_THING_TYPES_UIDS.stream())
.reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toSet())), 30);
super(Collections
.unmodifiableSet(Stream
.of(SUPPORTED_BUTTON_THING_TYPES_UIDS, SUPPORTED_HEATING_THING_TYPES,
SUPPORTED_DEVICE_THING_TYPES_UIDS, SUPPORTED_GROUP_THING_TYPES_UIDS)
.flatMap(Set::stream).collect(Collectors.toSet())),
30);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public BigDecimal getBatterylow() {
public String toString() {
return new StringBuilder().append("[ain=").append(ident).append(",bitmask=").append(bitmask)
.append(",isHANFUNDevice=").append(isHANFUNDevice()).append(",isHANFUNButton=").append(isHANFUNButton())
.append(",isHANFUNAlarmSensor=").append(isHANFUNAlarmSensor()).append("isButton").append(isButton())
.append(",isHANFUNAlarmSensor=").append(isHANFUNAlarmSensor()).append(",isButton").append(isButton())
.append(",isSwitchableOutlet=").append(isSwitchableOutlet()).append(",isTempSensor=")
.append(isTempSensor()).append(",isPowermeter=").append(isPowermeter()).append(",isDectRepeater=")
.append(isDectRepeater()).append(",isHeatingThermostat=").append(isHeatingThermostat())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.eclipse.jdt.annotation.Nullable;

/**
* See {@link DeviceListModel}.
Expand All @@ -30,8 +31,6 @@
* @author Christoph Weitkamp - Added channel 'battery_level'
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "tist", "tsoll", "absenk", "komfort", "lock", "devicelock", "errorcode", "batterylow",
"windowopenactiv", "battery", "nextchange", "summeractive", "holidayactive" })
@XmlRootElement(name = "hkr")
public class HeatingModel implements BatteryModel {
public static final BigDecimal TEMP_FACTOR = new BigDecimal("0.5");
Expand All @@ -52,7 +51,10 @@ public class HeatingModel implements BatteryModel {
private BigDecimal devicelock;
private String errorcode;
private BigDecimal batterylow;
private BigDecimal windowopenactiv;
private @Nullable BigDecimal windowopenactiv;
private @Nullable BigDecimal windowopenactiveendtime;
private @Nullable BigDecimal boostactive;
private @Nullable BigDecimal boostactiveendtime;
private BigDecimal battery;
private NextChangeModel nextchange;
private BigDecimal summeractive;
Expand Down Expand Up @@ -91,7 +93,7 @@ public void setAbsenk(BigDecimal absenk) {
}

public String getMode() {
if (getHolidayactive() != null && BigDecimal.ONE.equals(getHolidayactive())) {
if (BigDecimal.ONE.equals(getHolidayactive())) {
return MODE_VACATION;
} else if (getNextchange() != null && getNextchange().getEndperiod() != 0) {
return MODE_AUTO;
Expand All @@ -107,13 +109,13 @@ public String getRadiatorMode() {
return MODE_ON;
} else if (TEMP_FRITZ_OFF.compareTo(tsoll) == 0) {
return MODE_OFF;
} else if (getWindowopenactiv() != null && BigDecimal.ONE.equals(getWindowopenactiv())) {
} else if (BigDecimal.ONE.equals(getWindowopenactiv())) {
return MODE_WINDOW_OPEN;
} else if (tsoll.compareTo(komfort) == 0) {
return MODE_COMFORT;
} else if (tsoll.compareTo(absenk) == 0) {
return MODE_ECO;
} else if (TEMP_FRITZ_MAX.compareTo(tsoll) == 0) {
} else if (BigDecimal.ONE.equals(getBoostactive()) || TEMP_FRITZ_MAX.compareTo(tsoll) == 0) {
return MODE_BOOST;
} else {
return MODE_ON;
Expand Down Expand Up @@ -153,14 +155,26 @@ public void setBatterylow(BigDecimal batterylow) {
this.batterylow = batterylow;
}

public BigDecimal getWindowopenactiv() {
public @Nullable BigDecimal getWindowopenactiv() {
return windowopenactiv;
}

public @Nullable BigDecimal getWindowopenactiveendtime() {
return windowopenactiveendtime;
}

public void setWindowopenactiv(BigDecimal windowopenactiv) {
this.windowopenactiv = windowopenactiv;
}

public @Nullable BigDecimal getBoostactive() {
return boostactive;
}

public @Nullable BigDecimal getBoostactiveendtime() {
return boostactiveendtime;
}

@Override
public BigDecimal getBattery() {
return battery;
Expand Down Expand Up @@ -199,7 +213,9 @@ public String toString() {
return new StringBuilder().append("[tist=").append(tist).append(",tsoll=").append(tsoll).append(",absenk=")
.append(absenk).append(",komfort=").append(komfort).append(",lock=").append(lock).append(",devicelock=")
.append(devicelock).append(",errorcode=").append(errorcode).append(",batterylow=").append(batterylow)
.append(",windowopenactiv=").append(windowopenactiv).append(",battery=").append(battery)
.append(",windowopenactiv=").append(windowopenactiv).append(",windowopenactiveendtime=")
.append(windowopenactiveendtime).append(",boostactive=").append(boostactive)
.append(",boostactiveendtime=").append(boostactiveendtime).append(",battery=").append(battery)
.append(",nextchange=").append(nextchange).append(",summeractive=").append(summeractive)
.append(",holidayactive=").append(holidayactive).append("]").toString();
}
Expand Down Expand Up @@ -255,7 +271,6 @@ public static BigDecimal normalizeCelsius(BigDecimal celsiusValue) {
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "endperiod", "tchange" })
public static class NextChangeModel {
private int endperiod;
private BigDecimal tchange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ public void onDeviceListAdded(List<AVMFritzBaseModel> deviceList) {
String thingName = getThingName(device);

if (SUPPORTED_BUTTON_THING_TYPES_UIDS.contains(thingTypeUID)
|| SUPPORTED_HEATING_THING_TYPES.contains(thingTypeUID)
|| SUPPORTED_DEVICE_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new ThingUID(thingTypeUID, bridgeUID, thingName);
} else if (device.isHeatingThermostat()) {
Expand Down
Loading

0 comments on commit cfd2ae5

Please sign in to comment.