diff --git a/bundles/org.openhab.binding.avmfritz/README.md b/bundles/org.openhab.binding.avmfritz/README.md index 015375899947e..8e1c8251c26c8 100644 --- a/bundles/org.openhab.binding.avmfritz/README.md +++ b/bundles/org.openhab.binding.avmfritz/README.md @@ -92,6 +92,8 @@ The following sensors have been successfully tested using FRITZ!OS 7 for FRITZ!B - [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`) +- SmartHome LED-Lampe E27 (farbig) - a dimmable colorized light bulb (thing type `HAN_FUN_COLOR_BULB`) +- SmartHome LED-Lampe E27 (warmweiß) - a dimmable light bulb (thing type `HAN_FUN_DIMMABLE_BULB`) The use of other Sensors should be possible, if these are compatible with DECT-ULE / HAN-FUN standards. @@ -177,12 +179,14 @@ The AIN (actor identification number) can be found in the FRITZ!Box interface -> | locked | Contact | Device is locked for switching over external sources (OPEN/CLOSE) | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!Powerline 546E, FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT | | device_locked | Contact | Device is locked for switching manually (OPEN/CLOSE) - FRITZ!OS 6.90 | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!Powerline 546E, FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT | | temperature | Number:Temperature | Current measured temperature | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!DECT Repeater 100, FRITZ!DECT 301, FRITZ!DECT 300, Comet DECT, FRITZ!DECT 440 | -| humidity | Number:Dimensionless | Current measured humidity - FRITZ!OS 7.24 | FRITZ!DECT 440 | +| humidity | Number:Dimensionless | Current measured humidity - FRITZ!OS 7.24 | FRITZ!DECT 440 | | energy | Number:Energy | Accumulated energy consumption | FRITZ!DECT 210, FRITZ!DECT 200, FRITZ!Powerline 546E | | 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) | FRITZ!DECT 500, HAN_FUN_ON_OFF | +| on_off | Switch | Switchable device (ON/OFF) | HAN_FUN_ON_OFF | +| brightness | Dimmer | Dimmable lights | HAN_FUN_DIMMABLE_BULB | +| color | Color | Color lights | FRITZ!DECT 500, HAN_FUN_COLOR_BULB | | 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 | @@ -194,7 +198,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 | +| 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 c9c7354e65230..0c30a57e2528f 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 @@ -55,6 +55,8 @@ public class AVMFritzBindingConstants { 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"; + public static final String DEVICE_HAN_FUN_COLOR_BULB = "HAN_FUN_COLOR_BULB"; + public static final String DEVICE_HAN_FUN_DIMMABLE_BULB = "HAN_FUN_DIMMABLE_BULB"; // List of main group types public static final String GROUP_HEATING = "FRITZ_GROUP_HEATING"; @@ -78,6 +80,10 @@ public class AVMFritzBindingConstants { 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 HAN_FUN_COLOR_BULB_THING_TYPE = new ThingTypeUID(BINDING_ID, + DEVICE_HAN_FUN_COLOR_BULB); + public static final ThingTypeUID HAN_FUN_DIMMABLE_BULB_THING_TYPE = new ThingTypeUID(BINDING_ID, + DEVICE_HAN_FUN_DIMMABLE_BULB); 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); @@ -132,9 +138,9 @@ 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"; public static final String CHANNEL_COLOR = "color"; public static final String CHANNEL_BRIGHTNESS = "brightness"; + public static final String CHANNEL_ON_OFF = "on_off"; // List of all Channel config ids public static final String CONFIG_CHANNEL_TEMP_OFFSET = "offset"; @@ -167,7 +173,8 @@ public class AVMFritzBindingConstants { public static final String MODE_WINDOW_OPEN = "WINDOW_OPEN"; public static final String MODE_UNKNOWN = "UNKNOWN"; - public static final Set SUPPORTED_LIGHTING_THING_TYPES = Set.of(DECT500_THING_TYPE); + public static final Set SUPPORTED_LIGHTING_THING_TYPES = Set.of(DECT500_THING_TYPE, + HAN_FUN_COLOR_BULB_THING_TYPE, HAN_FUN_DIMMABLE_BULB_THING_TYPE); public static final Set SUPPORTED_BUTTON_THING_TYPES_UIDS = Set.of(DECT400_THING_TYPE, DECT440_THING_TYPE, HAN_FUN_SWITCH_THING_TYPE); diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModel.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModel.java index 37a9522df3eda..9afd0eec3727b 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModel.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModel.java @@ -17,6 +17,8 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import org.openhab.core.library.types.PercentType; + /** * See {@link DeviceListModel}. * @@ -26,6 +28,8 @@ @XmlRootElement(name = "colorcontrol") public class ColorControlModel { + private static final double SATURATION_FACTOR = 2.54; + @XmlAttribute(name = "supported_modes") public int supportedModes; @XmlAttribute(name = "current_mode") @@ -34,10 +38,40 @@ public class ColorControlModel { public int saturation; public int temperature; + /** + * Converts a FRITZ!Box value to a percent value. + * + * @param fritzValue The FRITZ!Box value to be converted + * @return The percent value + */ + public static PercentType toPercent(int saturation) { + int saturationInPercent = (int) Math.ceil(saturation / SATURATION_FACTOR); + return restrictToBounds(saturationInPercent); + } + + /** + * Converts a percent value to a FRITZ!Box value. + * + * @param saturationInPercent The percent value to be converted + * @return The FRITZ!Box value + */ + public static int fromPercent(PercentType saturationInPercent) { + return (int) Math.floor(saturationInPercent.intValue() * SATURATION_FACTOR); + } + @Override public String toString() { return new StringBuilder("[supportedModes=").append(supportedModes).append(",currentMode=").append(currentMode) .append(",hue=").append(hue).append(",saturation=").append(saturation).append(",temperature=") .append(temperature).append("]").toString(); } + + private static PercentType restrictToBounds(int percentValue) { + if (percentValue < 0) { + return PercentType.ZERO; + } else if (percentValue > 100) { + return PercentType.HUNDRED; + } + return new PercentType(percentValue); + } } diff --git a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/HeatingModel.java b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/HeatingModel.java index 21390dc21af63..62d3640c5f8e6 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/HeatingModel.java +++ b/bundles/org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/dto/HeatingModel.java @@ -240,12 +240,10 @@ public static BigDecimal fromCelsius(BigDecimal celsiusValue) { } /** - * Converts a celsius value to a FRITZ!Box value. - * Valid celsius values: 8 to 28 °C > 16 to 56 - * 16 <= 8°C, 17 = 8.5°C...... 56 >= 28°C, 254 = ON, 253 = OFF + * Converts a FRITZ!Box value to a celsius value. * - * @param celsiusValue The celsius value to be converted - * @return The FRITZ!Box value + * @param fritzValue The FRITZ!Box value to be converted + * @return The celsius value */ public static BigDecimal toCelsius(BigDecimal fritzValue) { if (fritzValue == null) { 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 4656fa79d846b..db9548093658c 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 @@ -329,6 +329,10 @@ public String getThingTypeId(AVMFritzBaseModel device) { } else if (device instanceof DeviceModel && device.isHANFUNUnit()) { if (device.isHANFUNBlinds()) { return DEVICE_HAN_FUN_BLINDS; + } else if (device.isColorLight()) { + return DEVICE_HAN_FUN_COLOR_BULB; + } else if (device.isDimmableLight()) { + return DEVICE_HAN_FUN_DIMMABLE_BULB; } List interfaces = Arrays .asList(((DeviceModel) device).getEtsiunitinfo().getInterfaces().split(",")); 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 9befbbd8ab782..c587b45ce4fa9 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 @@ -143,9 +143,6 @@ 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.isTemperatureSensor()) { @@ -164,6 +161,8 @@ public void onDeviceUpdated(ThingUID thingUID, AVMFritzBaseModel device) { updateColorLight(deviceModel.getColorControlModel(), deviceModel.getLevelControlModel()); } else if (deviceModel.isDimmableLight()) { updateDimmableLight(deviceModel.getLevelControlModel()); + } else if (device.isHANFUNUnit() && device.isHANFUNOnOff()) { + updateSimpleOnOffUnit(device.getSimpleOnOffUnit()); } } } @@ -208,10 +207,9 @@ private void updateColorLight(@Nullable ColorControlModel colorControlModel, @Nullable LevelControlModel levelControlModel) { if (colorControlModel != null && levelControlModel != null) { DecimalType hue = new DecimalType(colorControlModel.hue); - PercentType saturation = new PercentType(colorControlModel.saturation); + PercentType saturation = ColorControlModel.toPercent(colorControlModel.saturation); PercentType brightness = new PercentType(levelControlModel.getLevelPercentage()); updateThingChannelState(CHANNEL_COLOR, new HSBType(hue, saturation, brightness)); - updateThingChannelState(CHANNEL_BRIGHTNESS, brightness); } } @@ -414,10 +412,12 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof HSBType) { HSBType hsbType = (HSBType) command; brightness = hsbType.getBrightness().toBigDecimal(); - fritzBox.setHueAndSaturation(ain, hsbType.getHue().intValue(), hsbType.getSaturation().intValue(), - 0); + fritzBox.setHueAndSaturation(ain, hsbType.getHue().intValue(), + ColorControlModel.fromPercent(hsbType.getSaturation()), 0); } else if (command instanceof PercentType) { brightness = ((PercentType) command).toBigDecimal(); + } else if (command instanceof OnOffType) { + fritzBox.setSwitch(ain, OnOffType.ON.equals(command)); } if (brightness != null) { fritzBox.setLevelPercentage(ain, brightness); diff --git a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz.properties b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz.properties index 3a60e32708cd2..5a2944b867237 100644 --- a/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz.properties +++ b/bundles/org.openhab.binding.avmfritz/src/main/resources/OH-INF/i18n/avmfritz.properties @@ -39,8 +39,12 @@ thing-type.avmfritz.FRITZ_Powerline_546E_Solo.label = FRITZ!Powerline 546E thing-type.avmfritz.FRITZ_Powerline_546E_Solo.description = A FRITZ!Powerline 546E with switchable outlet in stand-alone mode. thing-type.avmfritz.HAN_FUN_BLINDS.label = HAN-FUN Blinds thing-type.avmfritz.HAN_FUN_BLINDS.description = HAN-FUN blinds (e.g. RolloTron DECT 1213) +thing-type.avmfritz.HAN_FUN_COLOR_BULB.label = HAN-FUN Color Light +thing-type.avmfritz.HAN_FUN_COLOR_BULB.description = HAN-FUN color light (e.g SmartHome LED-Lampe E27 (farbig)). thing-type.avmfritz.HAN_FUN_CONTACT.label = HAN-FUN Contact thing-type.avmfritz.HAN_FUN_CONTACT.description = HAN-FUN contact (e.g. SmartHome Tür-/Fensterkontakt or SmartHome Bewegungsmelder). +thing-type.avmfritz.HAN_FUN_DIMMABLE_BULB.label = HAN-FUN Dimmable Light +thing-type.avmfritz.HAN_FUN_DIMMABLE_BULB.description = HAN-FUN dimmable light (e.g. SmartHome LED-Lampe E27 (warmweiß)). thing-type.avmfritz.HAN_FUN_ON_OFF.label = HAN-FUN On / Off Device thing-type.avmfritz.HAN_FUN_ON_OFF.description = HAN-FUN switchable device (e.g. SmartHome Zwischenstecker innen / SmartHome Zwischenstecker außen) thing-type.avmfritz.HAN_FUN_SWITCH.label = HAN-FUN Switch 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 291ca23f16048..8ff4a8e806e7b 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 @@ -14,8 +14,6 @@ FRITZ!DECT500 color light. - - @@ -314,6 +312,41 @@ + + + + + + + HAN-FUN color light (e.g SmartHome LED-Lampe E27 (farbig)). + + + + + + ain + + + + + + + + + + + + HAN-FUN dimmable light (e.g. SmartHome LED-Lampe E27 (warmweiß)). + + + + + + ain + + + + diff --git a/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModelTest.java b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModelTest.java new file mode 100644 index 0000000000000..a2076f4c2d34d --- /dev/null +++ b/bundles/org.openhab.binding.avmfritz/src/test/java/org/openhab/binding/avmfritz/internal/dto/ColorControlModelTest.java @@ -0,0 +1,48 @@ +/** + * 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 static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.core.library.types.PercentType; + +/** + * Tests for {@link ColorControlModel} methods. + * + * @author Christoph Weitkamp - Initial contribution + */ +@NonNullByDefault +class ColorControlModelTest { + + @Test + public void testColorControlModelSaturationConversionIsBijective() { + for (int percent = 0; percent <= 100; ++percent) { + PercentType percentType = new PercentType(percent); + int saturation = ColorControlModel.fromPercent(percentType); + assertThat(ColorControlModel.toPercent(saturation).intValue(), is(percent)); + } + } + + @Test + public void hsbSaturationAlwaysGreaterThanZero() { + // a saturation greater than 1 should result in a percentage greater than 1 + for (int saturation = 1; saturation <= 254; ++saturation) { + PercentType percentType = ColorControlModel.toPercent(saturation); + assertTrue(ColorControlModel.fromPercent(percentType) > 0); + } + } +} 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 ce927e9a6f5db..44907df305c9b 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 @@ -89,7 +89,7 @@ public void cleanUp() { @Test public void correctSupportedTypes() { - assertEquals(16, discovery.getSupportedThingTypes().size()); + assertEquals(18, discovery.getSupportedThingTypes().size()); assertTrue(discovery.getSupportedThingTypes().contains(DECT100_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(DECT200_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(DECT210_THING_TYPE)); @@ -104,6 +104,8 @@ public void correctSupportedTypes() { assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_SWITCH_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_ON_OFF_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_BLINDS_THING_TYPE)); + assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_COLOR_BULB_THING_TYPE)); + assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_DIMMABLE_BULB_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(GROUP_HEATING_THING_TYPE)); assertTrue(discovery.getSupportedThingTypes().contains(GROUP_SWITCH_THING_TYPE)); }