From f5b54a673e8ad6447901424c3269e7399847609c Mon Sep 17 00:00:00 2001 From: Andras Uhrin <47272385+andrasU@users.noreply.github.com> Date: Sat, 20 Apr 2024 18:14:54 +0200 Subject: [PATCH] [freeathomesystem] Initial contribution (#13703) Signed-off-by: Andras Uhrin --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + .../NOTICE | 13 + .../README.md | 143 +++ .../pom.xml | 17 + .../src/main/feature/feature.xml | 9 + .../FreeAtHomeSystemBindingConstants.java | 42 + .../FreeAtHomeSystemDiscoveryService.java | 136 +++ .../FreeAtHomeSystemHandlerFactory.java | 116 +++ .../FreeAtHomeBridgeHandlerConfiguration.java | 31 + .../FreeAtHomeDeviceHandlerConfiguration.java | 25 + .../datamodel/FreeAtHomeDatapoint.java | 116 +++ .../datamodel/FreeAtHomeDatapointGroup.java | 262 +++++ .../datamodel/FreeAtHomeDeviceChannel.java | 543 +++++++++++ .../FreeAtHomeDeviceDescription.java | 154 +++ .../handler/FreeAtHomeBridgeHandler.java | 897 ++++++++++++++++++ .../handler/FreeAtHomeDeviceHandler.java | 635 +++++++++++++ .../FreeAtHomeDeviceStateListener.java | 29 + .../type/FreeAtHomeChannelTypeProvider.java | 28 + .../FreeAtHomeChannelTypeProviderImpl.java | 65 ++ .../type/FreeAtHomeThingTypeProvider.java | 31 + .../type/FreeAtHomeThingTypeProviderImpl.java | 55 ++ .../internal/util/FidTranslationUtils.java | 362 +++++++ .../util/FreeAtHomeGeneralException.java | 73 ++ .../FreeAtHomeHttpCommunicationException.java | 74 ++ .../internal/util/PIdContainerClass.java | 54 ++ .../internal/util/PidTranslationUtils.java | 829 ++++++++++++++++ .../internal/util/UidUtils.java | 44 + .../BinaryValueStateConverter.java | 60 ++ .../BooleanValueStateConverter.java | 45 + .../DecimalValueStateConverter.java | 37 + .../ShuttercontrolValueStateConverter.java | 79 ++ .../ValueStateConverter.java | 29 + .../src/main/resources/OH-INF/addon/addon.xml | 10 + .../OH-INF/i18n/freeathomesystem.properties | 652 +++++++++++++ .../resources/OH-INF/thing/bridge-type.xml | 35 + .../resources/OH-INF/thing/thing-type.xml | 24 + bundles/pom.xml | 1 + 38 files changed, 5761 insertions(+) create mode 100644 bundles/org.openhab.binding.freeathomesystem/NOTICE create mode 100644 bundles/org.openhab.binding.freeathomesystem/README.md create mode 100644 bundles/org.openhab.binding.freeathomesystem/pom.xml create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemBindingConstants.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemDiscoveryService.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemHandlerFactory.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeDeviceHandlerConfiguration.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapoint.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapointGroup.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceChannel.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceDescription.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeBridgeHandler.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceHandler.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceStateListener.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProvider.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProviderImpl.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProvider.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProviderImpl.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FidTranslationUtils.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeGeneralException.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeHttpCommunicationException.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PIdContainerClass.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PidTranslationUtils.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/UidUtils.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BinaryValueStateConverter.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BooleanValueStateConverter.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/DecimalValueStateConverter.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ShuttercontrolValueStateConverter.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ValueStateConverter.java create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/addon/addon.xml create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/i18n/freeathomesystem.properties create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/bridge-type.xml create mode 100644 bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/thing-type.xml diff --git a/CODEOWNERS b/CODEOWNERS index a3a4786ac56f1..ba7010f42f0d8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -114,6 +114,7 @@ /bundles/org.openhab.binding.folderwatcher/ @goopilot /bundles/org.openhab.binding.folding/ @fa2k /bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand +/bundles/org.openhab.binding.freeathomesystem/ @andrasU /bundles/org.openhab.binding.freebox/ @lolodomo /bundles/org.openhab.binding.freeboxos/ @clinique /bundles/org.openhab.binding.freecurrency/ @J-N-K diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 89ae0343c4401..edc4acc49766e 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -561,6 +561,11 @@ org.openhab.binding.foobot ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.freeathomesystem + ${project.version} + org.openhab.addons.bundles org.openhab.binding.freebox diff --git a/bundles/org.openhab.binding.freeathomesystem/NOTICE b/bundles/org.openhab.binding.freeathomesystem/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.freeathomesystem/README.md b/bundles/org.openhab.binding.freeathomesystem/README.md new file mode 100644 index 0000000000000..de9940c06d834 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/README.md @@ -0,0 +1,143 @@ +# ABB/Busch-free@home Smart Home binding + +openHAB ABB/Busch-free@home binding based on the offical free@home local API. + +# Description + +This binding allows you to connect your free@home Smart Home system from ABB / Busch-Jaeger to openHAB and to control and observe most of the components. +It requires a System Access Point with version 2.6.1 or higher. + +# Supported Devices + +**Network Gateway / System Access Point** + + - ABB / Busch-Jaeger System Access Point 2.0 + +**Sensors and Actuators** + + - Switch Actuator Sensor with single and multiple channels (wired and wireless) + - Dimming Actuator Sensor with single and multiple channels (wired, wireless and flex) + - Motion detection with and without actuator (wired, wireless and flex) + - Switch Actuator 4-channel + - Dimming Actuator 4- and 6-channel + - Door opener actuator + - Door ring sensor + - Hue devices (untested) + +**Blinds and Windows** + + - Shutter Actuator with single and multiple channels (wired and wireless) + - Blind Actuator with single and multiple channels (wired and wireless) + - Attic window actuator + - Awning actuator + +**Room Temperature Control** + + - Room temperature controller master without fan + - Room temperature controller master with fan + - Room temperature controller slave + +**Other devices** (e.g. movement detector, ring sensor and door opener) + + - IP-touch panel (function: door opener, door ring sensor) + - Virtual devices (e.g. virtual switch, RTC and detectors) + +**Information about virtual devices** +Virtual device in the free@home smart-home system needs continuous keep-alive signal otherwise the free@home device is marked as unresponsive. +This keep-alive signal must be provided by a user script or set the TTL value of the virtual device to "-1" during the creation of the virtual device. + +# Tested SysAP Versions + +| Version | Supported | +|---------|-----------| +| 2.6.1 | yes | +| 2.6.3 | yes | +| 3.1.1 | yes | + +# Setup / Installation + +## Prerequisites + +To make use of this Binding first the local free@home API has to be activated. +The API is disabled by default. + +1. Open the free@home next app +1. Browse to "Settings ⇨ free@home settings ⇨ local API and activate the checkbox + +## Setup and Discovery + +The free@home bridge shall be added manually. +Once it is added as a Thing with correct credentials, the scan of free@home devices will be possible. + +## free@home components as openHAB Things + +The ABB/Busch free@home system is calling its smart home components as free@home devices. +The free@home system devices can have one or multiple channels depending the device's features. +During the scanning process the openHAB binding will detect only the devices IDs. +The device features will be detected at the point in time, when a openHAB Thing is created. +At the of the creation the free@home binding will automatically create the relevant channels without any further configuration. +If a free@home system device has multiple smart-home channels (e.g. 4x DIN/rail Actuator), the newly created Thing will get all relevant channels to operate all actuators existing inside the free@home device. + +## Sensors and Actuators of free@home Devices as Things in openHAB + +The free@home system supports sensors and actuators. +The connection of sensors and actuators are done on the free@home system dashboard. +If a Thing channel is a free@home device sensor channel, this channel is read only. + +## Bridge Configuration + +There are several settings for a bridge: + +| Parameter | Description | +|--------------------------|-----------------------------------------| +| **ipAddress** (required) | Network address of the free@home SysAP | +| **username** (required) | Valid user name for the free@home SysAP | +| **password** (required) | Password of the user | + +## Examples for .things + +Things are all discovered automatically and visible on the openHAB UI after pushing the scan button + +In order to manually configure a Thing: + +```java +Bridge freeathomesystem:bridge:mysysap [ ipAddress="...", username="...", password="..." ] +{ + Thing device ABB700000001 + Thing device ABB700000012 +} +``` + +The only parameter needed to create a Thing is the free@home device ID, which you can find as sticker on the device. +The creation of the openHAB channels to operate the free@home device is happening automatically based on the device features detected online. + +## Examples for .items + +Sample for the free@home thermostat device + +```java +Switch Livingroom_Thermostat_Switch "Thermostat Siwtch" (Livingroom) { channel="freeathomesystem:device:312095ad75:ABB700000001:ch0000#controller-on-off-request" } +Switch LivingRoom_Thermostat_EcoOnOff "Thermostat Eco Activation" (Livingroom) { channel="freeathomesystem:device:312095ad75:ABB700000001:ch0000#eco-mode-on-off-request" } +Number LivingRoom_Thermostat_MeasuredTemperature "Measured Temperature" (Livingroom) ["Temperature"] { channel="freeathomesystem:device:312095ad75:ABB700000001:ch0000#measured-temperature" } +Number LivingRoom_Thermostat_SetpointTemperature "Setpoint Temperature" (Livingroom) ["Setpoint", "Temperature"] { channel="freeathomesystem:device:312095ad75:ABB700000001:ch0000#absolute-setpoint-temperature" } +Number LivingRoom_ThermostatHeatingActive "Thermostat Heating Active" (Livingroom) ["Status"] { channel="freeathomesystem:device:312095ad75:ABB700000001:ch0000#heating-active" } +Number LivingRoom_ThermostatHeatingDemand "Thermostat Heating Demand" (Livingroom) ["Status"] { channel="freeathomesystem:device:312095ad75:ABB700000001:ch0000#status-indication" } +``` + +Sample for the free@home device for switch + +```java +Switch Livingroom_Switch "Livingroom Switch" (Livingroom) ["Light"] { channel="freeathomesystem:device:312095ad75:ABB700000012:ch0000#switch-on-off" } +Switch Livingroom_Lamp "Livingroom Lamp" (Livingroom) ["Light"] { channel="freeathomesystem:device:312095ad75:ABB700000012:ch0006#switch-on-off" } +Switch Livingroom_Aux "Livingroom Aux Switch" (Livingroom) ["Light"] { channel="freeathomesystem:device:312095ad75:ABB700000012:ch000b#switch-on-off" } +``` + +# Communities + +[openHAB community of this binding](https://community.openhab.org/t/abb-busch-jager-free-home-official-rest-api/141698) + +[Busch-Jaeger Community](https://community.busch-jaeger.de/) + +[free@home user group Facebook DE](https://www.facebook.com/groups/738242583015188) + +[free@home user group Facebook EN](https://www.facebook.com/groups/452502972031360) diff --git a/bundles/org.openhab.binding.freeathomesystem/pom.xml b/bundles/org.openhab.binding.freeathomesystem/pom.xml new file mode 100644 index 0000000000000..dc49e47ee5846 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.2.0-SNAPSHOT + + + org.openhab.binding.freeathomesystem + + openHAB Add-ons :: Bundles :: FreeAtHomeSystem Binding + + diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/feature/feature.xml b/bundles/org.openhab.binding.freeathomesystem/src/main/feature/feature.xml new file mode 100644 index 0000000000000..323d77d1a557d --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.freeathomesystem/${project.version} + + diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemBindingConstants.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemBindingConstants.java new file mode 100644 index 0000000000000..359cc2c67a6cf --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemBindingConstants.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link FreeAtHomeSystemBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Andras Uhrin - Initial contribution + */ +@NonNullByDefault +public class FreeAtHomeSystemBindingConstants { + + public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing-type"; + + public static final String BINDING_ID = "freeathomesystem"; + + // List of all Thing Type UIDs + public static final String BRIDGE_TYPE_ID = "gateway"; + public static final String DEVICE_TYPE_ID = "device"; + + // List of all Thing Type UIDs + public static final ThingTypeUID BRIDGE_TYPE_UID = new ThingTypeUID(BINDING_ID, BRIDGE_TYPE_ID); + public static final ThingTypeUID DEVICE_TYPE_UID = new ThingTypeUID(BINDING_ID, DEVICE_TYPE_ID); + + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(BRIDGE_TYPE_UID, DEVICE_TYPE_UID); +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemDiscoveryService.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemDiscoveryService.java new file mode 100644 index 0000000000000..db1ff2c48405e --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemDiscoveryService.java @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal; + +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDeviceDescription; +import org.openhab.binding.freeathomesystem.internal.handler.FreeAtHomeBridgeHandler; +import org.openhab.binding.freeathomesystem.internal.util.FreeAtHomeHttpCommunicationException; +import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link FreeAtHomeSystemDiscoveryService} is responsible for performing discovery of things + * + * @author Andras Uhrin - Initial contribution + */ +@NonNullByDefault +public class FreeAtHomeSystemDiscoveryService extends AbstractThingHandlerDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeSystemDiscoveryService.class); + private @Nullable ScheduledFuture backgroundDiscoveryJob = null; + + private static final long BACKGROUND_DISCOVERY_DELAY = 1L; + private boolean isScanTerminated; + + Runnable runnable = new Runnable() { + @Override + public void run() { + ThingUID bridgeUID = thingHandler.getThing().getUID(); + + List deviceList; + + try { + deviceList = thingHandler.getDeviceDeviceList(); + + for (int i = 0; (i < deviceList.size()) && !isScanTerminated; i++) { + FreeAtHomeDeviceDescription device = thingHandler.getFreeatHomeDeviceDescription(deviceList.get(i)); + + ThingUID uid = new ThingUID(FreeAtHomeSystemBindingConstants.DEVICE_TYPE_UID, bridgeUID, + device.deviceId); + Map properties = new HashMap<>(1); + properties.put("deviceId", device.deviceId); + properties.put("interface", device.interfaceType); + + String deviceLabel = device.deviceLabel; + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withLabel(deviceLabel) + .withRepresentationProperty("deviceId").withBridge(bridgeUID).withProperties(properties) + .build(); + + thingDiscovered(discoveryResult); + + logger.debug("Thing discovered - DeviceId: {} - Device label: {}", device.getDeviceId(), + device.getDeviceLabel()); + } + + stopScan(); + } catch (FreeAtHomeHttpCommunicationException e) { + logger.debug("Communication error in device discovery with the bridge: {}", + thingHandler.getThing().getLabel()); + } catch (RuntimeException e) { + logger.debug("Scanning interrupted"); + } + } + }; + + public FreeAtHomeSystemDiscoveryService(int timeout) { + super(FreeAtHomeBridgeHandler.class, FreeAtHomeSystemBindingConstants.SUPPORTED_THING_TYPES_UIDS, timeout, + false); + } + + public FreeAtHomeSystemDiscoveryService() { + this(90); + } + + @Override + public Set getSupportedThingTypes() { + return Set.of(FreeAtHomeSystemBindingConstants.BRIDGE_TYPE_UID); + } + + @Override + protected void startScan() { + if (backgroundDiscoveryJob == null) { + this.removeOlderResults(Instant.now().toEpochMilli()); + + isScanTerminated = false; + backgroundDiscoveryJob = scheduler.schedule(runnable, BACKGROUND_DISCOVERY_DELAY, TimeUnit.SECONDS); + } + } + + @Override + protected synchronized void stopScan() { + super.stopScan(); + + isScanTerminated = true; + + ScheduledFuture localDiscoveryJob = backgroundDiscoveryJob; + + if (localDiscoveryJob != null) { + localDiscoveryJob.cancel(true); + } + + backgroundDiscoveryJob = null; + + removeOlderResults(Instant.now().toEpochMilli()); + } + + @Override + public void deactivate() { + removeOlderResults(Instant.now().toEpochMilli()); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemHandlerFactory.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemHandlerFactory.java new file mode 100644 index 0000000000000..10df82cec6a52 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/FreeAtHomeSystemHandlerFactory.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal; + +import static org.openhab.binding.freeathomesystem.internal.FreeAtHomeSystemBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.freeathomesystem.internal.handler.FreeAtHomeBridgeHandler; +import org.openhab.binding.freeathomesystem.internal.handler.FreeAtHomeDeviceHandler; +import org.openhab.binding.freeathomesystem.internal.type.FreeAtHomeChannelTypeProvider; +import org.openhab.binding.freeathomesystem.internal.type.FreeAtHomeThingTypeProvider; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link FreeAtHomeSystemHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Andras Uhrin - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.freeathomesystem", service = ThingHandlerFactory.class) +public class FreeAtHomeSystemHandlerFactory extends BaseThingHandlerFactory { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeSystemHandlerFactory.class); + + private final HttpClient httpClient; + private final FreeAtHomeChannelTypeProvider channelTypeProvider; + private final TranslationProvider i18nProvider; + private final LocaleProvider localeProvider; + + @Activate + public FreeAtHomeSystemHandlerFactory(@Reference FreeAtHomeThingTypeProvider thingTypeProvider, + @Reference FreeAtHomeChannelTypeProvider channelTypeProvider, @Reference TranslationProvider i18nProvider, + @Reference LocaleProvider localeProvider, @Reference HttpClientFactory httpClientFactory, + ComponentContext componentContext) { + super.activate(componentContext); + this.channelTypeProvider = channelTypeProvider; + this.i18nProvider = i18nProvider; + this.localeProvider = localeProvider; + + // create httpClient + httpClient = httpClientFactory.createHttpClient(FreeAtHomeSystemBindingConstants.BINDING_ID); + + // Configure client + httpClient.setFollowRedirects(false); + httpClient.setMaxConnectionsPerDestination(1); + httpClient.setMaxRequestsQueuedPerDestination(50); + + // Set timeouts + httpClient.setIdleTimeout(-1); + httpClient.setConnectTimeout(5000); + + try { + // Start HttpClient. + httpClient.start(); + } catch (Exception ex) { + logger.error("Could not create HttpClient: {}", ex.getMessage()); + + throw new IllegalStateException("Could not create HttpClient", ex); + } + } + + @Deactivate + public void deactivate() { + try { + httpClient.stop(); + } catch (Exception ex) { + logger.warn("Failed to stop HttpClient: {}", ex.getMessage()); + } + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (BRIDGE_TYPE_UID.equals(thingTypeUID)) { + return new FreeAtHomeBridgeHandler((Bridge) thing, httpClient); + } else if (DEVICE_TYPE_UID.equals(thingTypeUID)) { + return new FreeAtHomeDeviceHandler(thing, channelTypeProvider, i18nProvider, localeProvider); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java new file mode 100644 index 0000000000000..0d44df4ec6c05 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeBridgeHandlerConfiguration.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.configuration; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link FreeAtHomeBridgeHandlerConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Andras Uhrin - Initial contribution + */ +@NonNullByDefault +public class FreeAtHomeBridgeHandlerConfiguration { + + /** + * Bridgeconfiguration parameter. + */ + public String ipAddress = ""; + public String username = ""; + public String password = ""; +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeDeviceHandlerConfiguration.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeDeviceHandlerConfiguration.java new file mode 100644 index 0000000000000..8a84a5c6307fe --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/configuration/FreeAtHomeDeviceHandlerConfiguration.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.configuration; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link FreeAtHomeDeviceHandlerConfiguration} class contains fields mapping device configuration parameters. + * + * @author Andras Uhrin - Initial contribution + */ +@NonNullByDefault +public class FreeAtHomeDeviceHandlerConfiguration { + public String deviceId = ""; +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapoint.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapoint.java new file mode 100644 index 0000000000000..e21f9dcdd7fab --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapoint.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.datamodel; + +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeDatapoint { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeDatapoint.class); + + enum DatapointDirection { + UNKNOWN, + INPUT, + OUTPUT, + INPUTOUTPUT, + INPUT_AS_OUTPUT + }; + + public String channelId = ""; + private String datapointId = ""; + + DatapointDirection searchForDatapoint(DatapointDirection direction, int neededPairingIDFunction, String channelId, + JsonObject jsonObjectOfChannel) { + DatapointDirection resultingDirection = DatapointDirection.UNKNOWN; + boolean foundId = false; + JsonObject localDatapoints = null; + + switch (direction) { + case INPUT: { + localDatapoints = jsonObjectOfChannel.getAsJsonObject("inputs"); + resultingDirection = DatapointDirection.INPUT; + break; + } + case INPUT_AS_OUTPUT: { + localDatapoints = jsonObjectOfChannel.getAsJsonObject("inputs"); + resultingDirection = DatapointDirection.OUTPUT; + break; + } + case OUTPUT: { + localDatapoints = jsonObjectOfChannel.getAsJsonObject("outputs"); + resultingDirection = DatapointDirection.OUTPUT; + break; + } + default: { + localDatapoints = jsonObjectOfChannel.getAsJsonObject("outputs"); + resultingDirection = DatapointDirection.OUTPUT; + break; + } + } + + Set keys = localDatapoints.keySet(); + + Iterator iter = keys.iterator(); + + // Scan datapoints for pairingID IDs + while (iter.hasNext() && !foundId) { + String datapointId = iter.next(); + + JsonObject datapointJsonObject = localDatapoints.getAsJsonObject(datapointId); + + int pairingIDFunction = datapointJsonObject.get("pairingID").getAsInt(); + + if (pairingIDFunction == neededPairingIDFunction) { + this.channelId = channelId; + this.datapointId = datapointId; + + logger.debug("Datapoint is found - channel {} - datapoint {}", this.channelId, this.datapointId); + + foundId = true; + } + } + + // id not found, add dummy + if (!foundId) { + this.channelId = ""; + this.datapointId = ""; + resultingDirection = DatapointDirection.UNKNOWN; + + logger.debug("Needed datapoint is not found - channel {} - pairingId {}", channelId, + neededPairingIDFunction); + } + + return resultingDirection; + } + + public String getChannelIdforDatapoint() { + return channelId; + } + + public String getDatapointId() { + return datapointId; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapointGroup.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapointGroup.java new file mode 100644 index 0000000000000..efea0d69f77d5 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDatapointGroup.java @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.datamodel; + +import static org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDatapoint.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.freeathomesystem.internal.util.FreeAtHomeGeneralException; +import org.openhab.binding.freeathomesystem.internal.util.PidTranslationUtils; +import org.openhab.binding.freeathomesystem.internal.valuestateconverter.BooleanValueStateConverter; +import org.openhab.binding.freeathomesystem.internal.valuestateconverter.DecimalValueStateConverter; +import org.openhab.binding.freeathomesystem.internal.valuestateconverter.ShuttercontrolValueStateConverter; +import org.openhab.binding.freeathomesystem.internal.valuestateconverter.ValueStateConverter; +import org.openhab.core.library.CoreItemFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeDatapointGroup { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeDatapointGroup.class); + + public enum DatapointGroupDirection { + UNDEFINED, + INPUT, + OUTPUT, + INPUTOUTPUT + } + + private DatapointGroupDirection datapointGroupDirection; + private int pairingId; + + private String functionString = ""; + private @Nullable FreeAtHomeDatapoint inputDatapoint; + private @Nullable FreeAtHomeDatapoint outputDatapoint; + + FreeAtHomeDatapointGroup() { + datapointGroupDirection = DatapointGroupDirection.UNDEFINED; + inputDatapoint = null; + outputDatapoint = null; + } + + boolean addDatapointToGroup(DatapointDirection direction, int neededPairingId, String channelId, + JsonObject jsonObjectOfChannel) { + FreeAtHomeDatapoint newDatapoint = new FreeAtHomeDatapoint(); + + DatapointDirection resultingDirection = newDatapoint.searchForDatapoint(direction, neededPairingId, channelId, + jsonObjectOfChannel); + + if (resultingDirection != DatapointDirection.UNKNOWN) { + switch (resultingDirection) { + case INPUT: { + inputDatapoint = newDatapoint; + + pairingId = neededPairingId; + + break; + } + case OUTPUT: { + outputDatapoint = newDatapoint; + + if (inputDatapoint == null) { + pairingId = neededPairingId; + } + + break; + } + case INPUTOUTPUT: + case INPUT_AS_OUTPUT: + case UNKNOWN: + break; + } + } + + if (inputDatapoint != null && outputDatapoint != null) { + datapointGroupDirection = DatapointGroupDirection.INPUTOUTPUT; + } else { + if (inputDatapoint == null && outputDatapoint != null) { + datapointGroupDirection = DatapointGroupDirection.OUTPUT; + } else { + if (inputDatapoint != null && outputDatapoint == null) { + datapointGroupDirection = DatapointGroupDirection.INPUT; + } else { + datapointGroupDirection = DatapointGroupDirection.UNDEFINED; + } + } + } + + return resultingDirection != DatapointDirection.UNKNOWN; + } + + public void applyChangesForVirtualDevice() { + // The input and output datapoints are meant from the device point of view. Because the virtual devices are + // outside of the free@home system the input and output datapoint must be switched + @Nullable + FreeAtHomeDatapoint localDatapoint = inputDatapoint; + + inputDatapoint = outputDatapoint; + outputDatapoint = localDatapoint; + + if (inputDatapoint != null && outputDatapoint != null) { + datapointGroupDirection = DatapointGroupDirection.INPUTOUTPUT; + } else { + if (inputDatapoint == null && outputDatapoint != null) { + datapointGroupDirection = DatapointGroupDirection.OUTPUT; + } else { + if (inputDatapoint != null && outputDatapoint == null) { + datapointGroupDirection = DatapointGroupDirection.INPUT; + } else { + datapointGroupDirection = DatapointGroupDirection.UNDEFINED; + } + } + } + + return; + } + + public @Nullable FreeAtHomeDatapoint getInputDatapoint() { + return inputDatapoint; + } + + public @Nullable FreeAtHomeDatapoint getOutputDatapoint() { + return outputDatapoint; + } + + public DatapointGroupDirection getDirection() { + return datapointGroupDirection; + } + + public boolean isDecimal() throws FreeAtHomeGeneralException { + return PidTranslationUtils.PID_VALUETYPE_DECIMAL + .equals(PidTranslationUtils.getValueTypeForPairingId(String.format("0x%04X", pairingId))); + } + + public boolean isInteger() throws FreeAtHomeGeneralException { + return PidTranslationUtils.PID_VALUETYPE_INTEGER + .equals(PidTranslationUtils.getValueTypeForPairingId(String.format("0x%04X", pairingId))); + } + + public boolean isReadOnly() { + return (DatapointGroupDirection.INPUTOUTPUT == datapointGroupDirection) ? false + : ((DatapointGroupDirection.INPUT == datapointGroupDirection) ? false : true); + } + + public int getMax() throws FreeAtHomeGeneralException { + return PidTranslationUtils.getMax(String.format("0x%04X", pairingId)); + } + + public int getMin() throws FreeAtHomeGeneralException { + return PidTranslationUtils.getMin(String.format("0x%04X", pairingId)); + } + + public String getLabel() throws FreeAtHomeGeneralException { + return PidTranslationUtils.getShortTextForPairingId(String.format("0x%04X", pairingId)); + } + + public String getDescription() throws FreeAtHomeGeneralException { + return PidTranslationUtils.getDescriptionTextForPairingId(String.format("0x%04X", pairingId)); + } + + public String getValueType() throws FreeAtHomeGeneralException { + return PidTranslationUtils.getValueTypeForPairingId(String.format("0x%04X", pairingId)); + } + + public String getTypePattern() throws FreeAtHomeGeneralException { + String pattern = ""; + + functionString = PidTranslationUtils.getValueTypeForPairingId(String.format("0x%04X", pairingId)); + + switch (functionString) { + case PidTranslationUtils.PID_VALUETYPE_DECIMAL: + pattern = "%.1f"; + break; + case PidTranslationUtils.PID_VALUETYPE_INTEGER: + pattern = "%d"; + break; + default: + pattern = ""; + logger.debug("Type pattern not found for PairingID {} - using default", + String.format("0x%04X", pairingId)); + break; + } + + return pattern; + } + + public ValueStateConverter getValueStateConverter() throws FreeAtHomeGeneralException { + ValueStateConverter valueStateConverter = new BooleanValueStateConverter(); + + functionString = PidTranslationUtils.getValueTypeForPairingId(String.format("0x%04X", pairingId)); + + switch (functionString) { + case PidTranslationUtils.PID_VALUETYPE_BOOLEAN: + valueStateConverter = new BooleanValueStateConverter(); + break; + case PidTranslationUtils.PID_VALUETYPE_DECIMAL: + case PidTranslationUtils.PID_VALUETYPE_INTEGER: + valueStateConverter = new DecimalValueStateConverter(); + break; + case PidTranslationUtils.PID_VALUETYPE_STRING: + break; + case PidTranslationUtils.PID_VALUETYPE_SHUTTERMOVEMENT: + valueStateConverter = new ShuttercontrolValueStateConverter(); + break; + default: + valueStateConverter = new DecimalValueStateConverter(); + logger.debug("Value converter not found for PairingID {} - using default", + String.format("0x%04X", pairingId)); + break; + } + + return valueStateConverter; + } + + public String getOpenHabItemType() throws FreeAtHomeGeneralException { + String itemTypeString = ""; + + functionString = PidTranslationUtils.getValueTypeForPairingId(String.format("0x%04X", pairingId)); + + switch (functionString) { + case PidTranslationUtils.PID_VALUETYPE_BOOLEAN: + itemTypeString = CoreItemFactory.SWITCH; + break; + case PidTranslationUtils.PID_VALUETYPE_DECIMAL: + case PidTranslationUtils.PID_VALUETYPE_INTEGER: + itemTypeString = CoreItemFactory.NUMBER; + break; + case PidTranslationUtils.PID_VALUETYPE_SHUTTERMOVEMENT: + itemTypeString = CoreItemFactory.ROLLERSHUTTER; + break; + default: + itemTypeString = CoreItemFactory.NUMBER; + logger.debug("Item type constant not found for PairingID {} - using default", + String.format("0x%04X", pairingId)); + break; + } + + return itemTypeString; + } + + public String getOpenHabCategory() throws FreeAtHomeGeneralException { + return PidTranslationUtils.getCategoryForPairingId(String.format("0x%04X", pairingId)); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceChannel.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceChannel.java new file mode 100644 index 0000000000000..ef75942c224bf --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceChannel.java @@ -0,0 +1,543 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.datamodel; + +import static org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDatapoint.*; +import static org.openhab.binding.freeathomesystem.internal.util.FidTranslationUtils.*; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.freeathomesystem.internal.util.FidTranslationUtils; +import org.openhab.binding.freeathomesystem.internal.util.FreeAtHomeGeneralException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; + +/** + * The {@link FreeAtHomeDeviceChannel} holding the information of device channels + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeDeviceChannel { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeDeviceChannel.class); + + private String channelLabel = ""; + private String channelId = ""; + private String channelFunctionID = ""; + + private List datapointGroups = new ArrayList<>(); + + public boolean createChannelFromJson(String deviceLabel, String channelId, JsonObject jsonObjectOfChannels, + boolean isScene, boolean isRule) { + JsonObject channelObject = jsonObjectOfChannels.getAsJsonObject(channelId); + + channelFunctionID = channelObject.get("functionID").getAsString(); + + // check whether this is a valid channel + if (channelFunctionID.isEmpty()) { + // invalid channel found + logger.info("Invalid channel function ID found - Devicelabel: {} Channel: {}", deviceLabel, channelId); + + return false; + } + + if (!channelFunctionID.isEmpty()) { + channelLabel = channelObject.get("displayName").getAsString(); + + if (channelLabel.isEmpty()) { + channelLabel = deviceLabel; + } + } + + if (isScene) { + channelFunctionID = channelFunctionID.substring(0, channelFunctionID.length() - 1) + "0"; + } + + switch (Integer.parseInt(channelFunctionID, 16)) { + case FID_PANEL_SWITCH_SENSOR: + case FID_SWITCH_SENSOR: { + this.channelId = channelId; + + logger.debug("Switch sensor channel found - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_PANEL_DIMMING_SENSOR: + case FID_DIMMING_SENSOR: { + this.channelId = channelId; + + logger.debug("Dimming sensor channel found - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 17, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_TRIGGER: + case FID_SWITCH_ACTUATOR: { + this.channelId = channelId; + + logger.debug("Switch actuator channel created - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 1, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_MOVEMENT_DETECTOR_FLEX: + case FID_MOVEMENT_DETECTOR: { + this.channelId = channelId; + + logger.debug("Movement detector channel found - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 6, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 7, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1027, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT_AS_OUTPUT, 256, channelId, + channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN: + case FID_RADIATOR_ACTUATOR_MASTER: { + this.channelId = channelId; + + if (Integer.parseInt(channelFunctionID, 16) == FID_RADIATOR_ACTUATOR_MASTER) { + logger.debug("Radiator actuator channel - Channel FID: {}", channelFunctionID); + } else { + logger.debug("Room temperature actuator channel - Channel FID: {}", channelFunctionID); + } + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 304, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 333, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 331, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 54, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 51, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 320, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 68, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 58, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 56, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 66, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + // Additional channel for RTC device + if (Integer.parseInt(channelFunctionID, 16) == FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN) { + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 48, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + } + + break; + } + case FID_WINDOW_DOOR_POSITION_SENSOR: + case FID_WINDOW_DOOR_SENSOR: { + this.channelId = channelId; + + logger.debug("Window/Door position sensor channel created - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 53, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 41, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_SCENE_TRIGGER: { + this.channelId = channelId; + + logger.debug("Scene trigger channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 4, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_RULE_SWITCH: { + this.channelId = channelId; + + logger.debug("Rule channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 61698, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 61697, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_DES_DOOR_OPENER_ACTUATOR: { + this.channelId = channelId; + + logger.debug("Door opener actuator channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 2, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_DES_LEVEL_CALL_SENSOR: + case FID_DES_DOOR_RINGING_SENSOR: { + this.channelId = channelId; + + logger.debug("Door ring sensor channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 2, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_DES_LIGHT_SWITCH_ACTUATOR: { + this.channelId = channelId; + + logger.debug("DES light switch channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 2, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_DIMMING_ACTUATOR_FLEX: + case FID_DIMMING_ACTUATOR: { + this.channelId = channelId; + + logger.debug("Dimming actuator channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 272, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 17, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 256, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 1, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AWNING_ACTUATOR: + case FID_ATTIC_WINDOW_ACTUATOR: + case FID_BLIND_ACTUATOR: + case FID_SHUTTER_ACTUATOR: { + this.channelId = channelId; + + logger.debug("Shutter actuator channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 32, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 288, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 33, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 288, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 289, channelId, channelObject); + newDatapointGroup.addDatapointToGroup(DatapointDirection.INPUT, 35, channelId, channelObject); + + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_BRIGHTNESS_SENSOR: { + this.channelId = channelId; + + logger.debug("Brightness sensor channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1026, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1027, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_RAIN_SENSOR: { + this.channelId = channelId; + + logger.debug("Rain sensor channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 39, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1029, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1030, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_TEMPERATURE_SENSOR: { + this.channelId = channelId; + + logger.debug("Temperature sensor channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + if (newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 38, channelId, channelObject)) { + datapointGroups.add(newDatapointGroup); + } + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + if (newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1024, channelId, channelObject)) { + datapointGroups.add(newDatapointGroup); + } + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + if (newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 304, channelId, channelObject)) { + datapointGroups.add(newDatapointGroup); + } + + break; + } + case FID_WIND_SENSOR: { + this.channelId = channelId; + + logger.debug("Wind sensor channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 37, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1025, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1028, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_CO: { + this.channelId = channelId; + + logger.debug("AQS CO channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1564, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_CO2: { + this.channelId = channelId; + + logger.debug("AQS CO2 channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1563, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_HUMIDITY: { + this.channelId = channelId; + + logger.debug("AQS Humidity channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 337, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_PRESSURE: { + this.channelId = channelId; + + logger.debug("AQS Pressure channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1562, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_NO2: { + this.channelId = channelId; + + logger.debug("AQS NO2 channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1565, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_O3: { + this.channelId = channelId; + + logger.debug("AQS O3 channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1566, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_PM10: { + this.channelId = channelId; + + logger.debug("AQS PM10 channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1567, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_VOC: { + this.channelId = channelId; + + logger.debug("AQS VOC channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1569, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + case FID_AIRQUALITYSENSOR_PM25: { + this.channelId = channelId; + + logger.debug("AQS PM25 channel - Channel FID: {}", channelFunctionID); + + FreeAtHomeDatapointGroup newDatapointGroup = new FreeAtHomeDatapointGroup(); + newDatapointGroup.addDatapointToGroup(DatapointDirection.OUTPUT, 1568, channelId, channelObject); + datapointGroups.add(newDatapointGroup); + + break; + } + default: { + logger.debug("Unknown channel found - Channel FID: {}", channelFunctionID); + + return false; + } + } + + return true; + } + + public String getChannelIdforDatapoint() { + return channelId; + } + + public String getChannelLabel() { + return channelLabel; + } + + public String getChannelId() { + return channelId; + } + + public String getFunctionId() { + return channelFunctionID; + } + + public @Nullable String getFunctionIdText() throws FreeAtHomeGeneralException { + String functionIdText = FidTranslationUtils + .getFunctionIdText(String.format("0x%04X", Integer.parseInt(channelFunctionID, 16))); + return functionIdText; + } + + public int getNumberOfDatapointGroup() { + return datapointGroups.size(); + } + + public FreeAtHomeDatapointGroup getDatapointGroup(int idx) { + return datapointGroups.get(idx); + } + + public void applyChangesForVirtualDevice() { + for (FreeAtHomeDatapointGroup localDatapointGroup : datapointGroups) { + localDatapointGroup.applyChangesForVirtualDevice(); + } + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceDescription.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceDescription.java new file mode 100644 index 0000000000000..2b50a83127e66 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/datamodel/FreeAtHomeDeviceDescription.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.datamodel; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * The {@link FreeAtHomeDeviceDescription} is responsible for determining the device type + * based on the received json string + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeDeviceDescription { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeDeviceDescription.class); + + // interface strings + public static final String DEVICE_INTERFACE_UNKNOWN_TYPE = "unknown"; + public static final String DEVICE_INTERFACE_WIRELESS_TYPE = "wireless"; + public static final String DEVICE_INTERFACE_VIRTUAL_TYPE = "virtual"; + public static final String DEVICE_INTERFACE_WIRED_TYPE = "wired"; + public static final String DEVICE_INTERFACE_HUE_TYPE = "hue"; + + public String deviceLabel = ""; + public String deviceId = ""; + public String interfaceType = ""; + public boolean validDevice = false; + + private boolean sceneIsDetected; + private boolean ruleIsDetected; + + public List listOfChannels = new ArrayList<>(); + + public FreeAtHomeDeviceDescription() { + validDevice = false; + } + + public FreeAtHomeDeviceDescription(JsonObject jsonObject, String id) { + // set the device ID + deviceId = id; + + // set the device invalid at first + validDevice = false; + + sceneIsDetected = id.toLowerCase().startsWith("ffff48"); + ruleIsDetected = id.toLowerCase().startsWith("ffff4a"); + + JsonObject jsonObjectOfId = jsonObject.getAsJsonObject(id); + + if (jsonObjectOfId == null) { + return; + } + + JsonElement jsonObjectOfInterface = jsonObjectOfId.get("interface"); + + if (jsonObjectOfInterface != null) { + String interfaceString = jsonObjectOfInterface.getAsString(); + + if (interfaceString.toLowerCase().startsWith("vdev:")) { + interfaceType = DEVICE_INTERFACE_VIRTUAL_TYPE; + } else if (interfaceString.toLowerCase().startsWith("hue")) { + interfaceType = DEVICE_INTERFACE_HUE_TYPE; + } else if (interfaceString.toLowerCase().startsWith("rf")) { + interfaceType = DEVICE_INTERFACE_WIRELESS_TYPE; + } else if (interfaceString.toLowerCase().startsWith("tp")) { + interfaceType = DEVICE_INTERFACE_WIRED_TYPE; + } else { + interfaceType = DEVICE_INTERFACE_UNKNOWN_TYPE; + } + } else { + interfaceType = DEVICE_INTERFACE_UNKNOWN_TYPE; + } + + JsonElement jsonObjectOfDeviceLabel = jsonObjectOfId.get("displayName"); + + if (jsonObjectOfDeviceLabel == null) { + this.deviceLabel = "NoName"; + } else { + this.deviceLabel = jsonObjectOfDeviceLabel.getAsString(); + } + + if (this.deviceLabel.isEmpty()) { + this.deviceLabel = "NoName"; + } + + JsonObject jsonObjectOfChannels = jsonObjectOfId.getAsJsonObject("channels"); + + logger.debug("Detecting device features - device id: {} - device label: {}", this.deviceId, this.deviceLabel); + + if (jsonObjectOfChannels != null) { + // Scan channels for functions + for (String nextChannel : jsonObjectOfChannels.keySet()) { + FreeAtHomeDeviceChannel newChannel = new FreeAtHomeDeviceChannel(); + + if (newChannel.createChannelFromJson(deviceLabel, nextChannel, jsonObjectOfChannels, sceneIsDetected, + ruleIsDetected)) { + if (interfaceType == DEVICE_INTERFACE_VIRTUAL_TYPE) { + newChannel.applyChangesForVirtualDevice(); + } + + listOfChannels.add(newChannel); + } + } + } + } + + public boolean isRule() { + return ruleIsDetected; + } + + public boolean isScene() { + return sceneIsDetected; + } + + public boolean isVirtual() { + return interfaceType == DEVICE_INTERFACE_VIRTUAL_TYPE; + } + + public int getNumberOfChannels() { + return listOfChannels.size(); + } + + public FreeAtHomeDeviceChannel getChannel(int idx) { + return listOfChannels.get(idx); + } + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceLabel() { + return deviceLabel; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeBridgeHandler.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeBridgeHandler.java new file mode 100644 index 0000000000000..73a7426fdcf55 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeBridgeHandler.java @@ -0,0 +1,897 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.handler; + +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.AuthenticationStore; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.BasicAuthentication; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.api.WebSocketListener; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.openhab.binding.freeathomesystem.internal.FreeAtHomeSystemDiscoveryService; +import org.openhab.binding.freeathomesystem.internal.configuration.FreeAtHomeBridgeHandlerConfiguration; +import org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDeviceDescription; +import org.openhab.binding.freeathomesystem.internal.util.FreeAtHomeHttpCommunicationException; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; + +/** + * The {@link FreeAtHomeBridgeHandler} is responsible for handling the free@home bridge and + * its main communication. + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeBridgeHandler extends BaseBridgeHandler implements WebSocketListener { + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeBridgeHandler.class); + + private Map mapEventListeners = new ConcurrentHashMap<>(); + + // Clients for the network communication + private HttpClient httpClient; + private @Nullable WebSocketClient websocketClient = null; + private FreeAtHomeWebsocketMonitorThread socketMonitor = new FreeAtHomeWebsocketMonitorThread(); + private @Nullable QueuedThreadPool jettyThreadPool = null; + private volatile @Nullable Session websocketSession = null; + + private String sysApUID = "00000000-0000-0000-0000-000000000000"; + private String ipAddress = ""; + private String username = ""; + private String password = ""; + + private String baseUrl = ""; + + private String authField = ""; + + private Lock lock = new ReentrantLock(); + private AtomicBoolean httpConnectionOK = new AtomicBoolean(false); + private Condition websocketSessionEstablished = lock.newCondition(); + + int numberOfComponents = 0; + + private static final int BRIDGE_WEBSOCKET_RECONNECT_DELAY = 60; + private static final int BRIDGE_WEBSOCKET_TIMEOUT = 90; + private static final int BRIDGE_WEBSOCKET_KEEPALIVE = 50; + private static final String BRIDGE_URL_GETDEVICELIST = "/rest/devicelist"; + + public FreeAtHomeBridgeHandler(Bridge thing, HttpClient client) { + super(thing); + + httpClient = client; + } + + /** + * stub method for handlCommand + */ + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.warn("Unknown handle command for the bridge - channellUID {}, command {}", channelUID, command); + } + + @Override + public Collection> getServices() { + return List.of(FreeAtHomeSystemDiscoveryService.class); + } + + /** + * Method to get the device list + */ + public List getDeviceDeviceList() throws FreeAtHomeHttpCommunicationException { + List listOfComponentId = new ArrayList(); + boolean ret = false; + + listOfComponentId.clear(); + + String url = baseUrl + BRIDGE_URL_GETDEVICELIST; + + // Perform a simple GET and wait for the response. + try { + HttpClient client = httpClient; + + Request req = client.newRequest(url); + + if (req == null) { + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid request object in getDeviceDeviceList with the URL [ " + url + " ]"); + } + + ContentResponse response = req.send(); + + // Get component List + String componentListString = new String(response.getContent()); + + JsonElement jsonTree = JsonParser.parseString(componentListString); + + // check the output + if (!jsonTree.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid jsonObject in getDeviceDeviceList with the URL [ " + url + " ]"); + } + + JsonObject jsonObject = jsonTree.getAsJsonObject(); + + // Get the main object + JsonElement listOfComponents = jsonObject.get(sysApUID); + + if (listOfComponents == null) { + throw new FreeAtHomeHttpCommunicationException(0, + "Devices Section is missing in getDeviceDeviceList with the URL [ " + url + " ]"); + } + + JsonArray array = listOfComponents.getAsJsonArray(); + + this.numberOfComponents = array.size(); + + for (int i = 0; i < array.size(); i++) { + JsonElement basicElement = array.get(i); + + listOfComponentId.add(basicElement.getAsString()); + } + + ret = true; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.debug("Error to build up the Component list [ {} ]", e.getMessage()); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted [ " + e.getMessage() + " ]"); + } catch (ExecutionException | TimeoutException e) { + logger.debug("Error to build up the Component list [ {} ]", e.getMessage()); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted in getDeviceList [ " + e.getMessage() + " ]"); + } + + // Scan finished but error. clear the list + if (!ret) { + listOfComponentId.clear(); + } + + return listOfComponentId; + } + + /** + * Method to send http request to get the device description + */ + public FreeAtHomeDeviceDescription getFreeatHomeDeviceDescription(String id) + throws FreeAtHomeHttpCommunicationException { + FreeAtHomeDeviceDescription device = new FreeAtHomeDeviceDescription(); + + String url = baseUrl + "/rest/device/" + sysApUID + "/" + id; + try { + HttpClient client = httpClient; + Request req = client.newRequest(url); + + if (req == null) { + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid request object in getDatapoint with the URL [ " + url + " ]"); + } + + ContentResponse response; + response = req.send(); + + // Get component List + String deviceString = new String(response.getContent()); + + JsonReader reader = new JsonReader(new StringReader(deviceString)); + reader.setLenient(true); + JsonElement jsonTree = JsonParser.parseReader(reader); + + if (!jsonTree.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "No data is received by getDatapoint with the URL [ " + url + " ]"); + } + + if (!jsonTree.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid jsonObject in getFreeatHomeDeviceDescription with the URL [ " + url + " ]"); + } + + // check the output + JsonObject jsonObject = jsonTree.getAsJsonObject(); + + if (!jsonObject.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "Main jsonObject is invalid in getFreeatHomeDeviceDescription with the URL [ " + url + " ]"); + } + + jsonObject = jsonObject.getAsJsonObject(sysApUID); + + if (!jsonObject.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "jsonObject is invalid in getFreeatHomeDeviceDescription with the URL [ " + url + " ]"); + } + + jsonObject = jsonObject.getAsJsonObject("devices"); + + if (!jsonObject.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "Devices Section is missing in getFreeatHomeDeviceDescription with the URL [ " + url + " ]"); + } + + device = new FreeAtHomeDeviceDescription(jsonObject, id); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.debug("No communication possible to get device list - Communication interrupt [ {} ]", + e.getMessage()); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted [ " + e.getMessage() + " ]"); + } catch (ExecutionException | TimeoutException e) { + logger.debug("No communication possible to get device list - Communication interrupt [ {} ]", + e.getMessage()); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted in getDeviceList [ " + e.getMessage() + " ]"); + } + + return device; + } + + /** + * Method to get datapoint values for devices + */ + public String getDatapoint(String deviceId, String channel, String datapoint) + throws FreeAtHomeHttpCommunicationException { + String url = baseUrl + "/rest/datapoint/" + sysApUID + "/" + deviceId + "." + channel + "." + datapoint; + + try { + Request req = httpClient.newRequest(url); + + logger.debug("Get datapoint by url: {}", url); + + if (req == null) { + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid request object in getDatapoint with the URL [ " + url + " ]"); + } + + ContentResponse response = req.send(); + + if (response.getStatus() != 200) { + throw new FreeAtHomeHttpCommunicationException(response.getStatus(), response.getReason()); + } + + String deviceString = new String(response.getContent()); + + JsonReader reader = new JsonReader(new StringReader(deviceString)); + reader.setLenient(true); + JsonElement jsonTree = JsonParser.parseReader(reader); + + if (!jsonTree.isJsonObject()) { + throw new FreeAtHomeHttpCommunicationException(0, + "No data is received by getDatapoint with the URL [ " + url + " ]"); + } + + JsonObject jsonObject = jsonTree.getAsJsonObject(); + + jsonObject = jsonObject.getAsJsonObject(sysApUID); + JsonArray jsonValueArray = jsonObject.getAsJsonArray("values"); + + JsonElement element = jsonValueArray.get(0); + String value = element.getAsString(); + + if (value.isEmpty()) { + value = "0"; + } + + return value; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted [ " + e.getMessage() + " ]"); + } catch (ExecutionException | TimeoutException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication timout or execution interrupted [ " + e.getMessage() + " ]"); + } catch (JsonParseException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid JSON file is received by getDatapoint with the URL [ " + e.getMessage() + " ]"); + } + } + + /** + * Method to set datapoint values in channels + */ + public boolean setDatapoint(String deviceId, String channel, String datapoint, String valueString) + throws FreeAtHomeHttpCommunicationException { + String url = baseUrl + "/rest/datapoint/" + sysApUID + "/" + deviceId + "." + channel + "." + datapoint; + + try { + Request req = httpClient.newRequest(url); + + if (req == null) { + throw new FreeAtHomeHttpCommunicationException(0, + "Invalid request object in getDatapoint with the URL [ " + url + " ]"); + } + + req.content(new StringContentProvider(valueString)); + req.method(HttpMethod.PUT); + + logger.debug("Set datapoint by url: {} value: {}", url, valueString); + + ContentResponse response = req.send(); + + if (response.getStatus() != 200) { + throw new FreeAtHomeHttpCommunicationException(response.getStatus(), response.getReason()); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted [ " + e.getMessage() + " ]"); + } catch (ExecutionException | TimeoutException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + + throw new FreeAtHomeHttpCommunicationException(0, + "Http communication interrupted [ " + e.getMessage() + " ]"); + } + + return true; + } + + /** + * Method to process socket events + */ + public void setDatapointOnWebsocketFeedback(String receivedText) { + JsonReader reader = new JsonReader(new StringReader(receivedText)); + reader.setLenient(true); + JsonElement jsonTree = JsonParser.parseReader(reader); + + // check the output + if (jsonTree.isJsonObject()) { + JsonObject jsonObject = jsonTree.getAsJsonObject(); + + jsonObject = jsonObject.getAsJsonObject(sysApUID); + jsonObject = jsonObject.getAsJsonObject("datapoints"); + + Set keys = jsonObject.keySet(); + + Iterator iter = keys.iterator(); + + while (iter.hasNext()) { + String eventDatapointID = iter.next(); + + JsonElement element = jsonObject.get(eventDatapointID); + String value = element.getAsString(); + + String[] parts = eventDatapointID.split("/"); + + FreeAtHomeDeviceHandler deviceHandler = mapEventListeners.get(parts[0]); + + if (deviceHandler != null) { + deviceHandler.onDeviceStateChanged(eventDatapointID, value); + } + + logger.debug("Socket event processed: event-datapoint-ID {} value {}", eventDatapointID, value); + } + } + } + + public void markDeviceRemovedOnWebsocketFeedback(String receivedText) { + JsonReader reader = new JsonReader(new StringReader(receivedText)); + reader.setLenient(true); + JsonElement jsonTree = JsonParser.parseReader(reader); + + // check the output + if (jsonTree.isJsonObject()) { + JsonObject jsonObject = jsonTree.getAsJsonObject(); + + jsonObject = jsonObject.getAsJsonObject(sysApUID); + JsonArray jsonArray = jsonObject.getAsJsonArray("devicesRemoved"); + + for (JsonElement element : jsonArray) { + FreeAtHomeDeviceHandler deviceHandler = mapEventListeners.get(element.getAsString()); + + if (deviceHandler != null) { + deviceHandler.onDeviceRemoved(); + + logger.debug("Device removal processed"); + } + } + } + } + + public void registerDeviceStateListener(String deviceID, FreeAtHomeDeviceHandler deviceHandler) { + mapEventListeners.put(deviceID, deviceHandler); + } + + public void unregisterDeviceStateListener(String deviceID) { + mapEventListeners.remove(deviceID); + } + + /** + * Method to open Http connection + */ + public boolean openHttpConnection() { + boolean ret = false; + + try { + // Add authentication credentials. + AuthenticationStore auth = httpClient.getAuthenticationStore(); + + URI uri1 = new URI(baseUrl); + auth.addAuthenticationResult(new BasicAuthentication.BasicResult(uri1, username, password)); + + String url = baseUrl + BRIDGE_URL_GETDEVICELIST; + + Request req = httpClient.newRequest(url); + ContentResponse res = req.send(); + + // check status + if (res.getStatus() == HttpStatus.OK_200) { + // response OK + httpConnectionOK.set(true); + + ret = true; + + logger.debug("HTTP connection to SysAP is OK"); + } else { + // response NOK, set error + httpConnectionOK.set(false); + + ret = false; + } + } catch (URISyntaxException | InterruptedException | ExecutionException | TimeoutException ex) { + logger.debug("Initial HTTP connection to SysAP is not successful"); + + ret = false; + } + + return ret; + } + + /** + * Method to connect the websocket session + */ + public boolean connectWebsocketSession() { + boolean ret = false; + + URI uri = URI.create("ws://" + ipAddress + "/fhapi/v1/api/ws"); + + String authString = username + ":" + password; + + // create base64 encoder + Base64.Encoder bas64Encoder = Base64.getEncoder(); + + // Encoding string using encoder object + String authStringEnc = bas64Encoder.encodeToString(authString.getBytes()); + + authField = "Basic " + authStringEnc; + + WebSocketClient localWebsocketClient = websocketClient; + + try { + // Start socket client + if (localWebsocketClient != null) { + localWebsocketClient.setMaxTextMessageBufferSize(8 * 1024); + localWebsocketClient.setMaxIdleTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 60 * 1000); + localWebsocketClient.setConnectTimeout(BRIDGE_WEBSOCKET_TIMEOUT * 60 * 1000); + localWebsocketClient.start(); + ClientUpgradeRequest request = new ClientUpgradeRequest(); + request.setHeader("Authorization", authField); + request.setTimeout(BRIDGE_WEBSOCKET_TIMEOUT, TimeUnit.MINUTES); + localWebsocketClient.connect(this, uri, request); + + logger.debug("Websocket connection to SysAP is OK, timeout: {}", BRIDGE_WEBSOCKET_TIMEOUT); + + ret = true; + } else { + ret = false; + } + } catch (Exception e) { + logger.debug("Error by opening Websocket connection [{}]", e.getMessage()); + + if (localWebsocketClient != null) { + try { + localWebsocketClient.stop(); + + ret = false; + } catch (Exception e1) { + logger.debug("Error by opening Websocket connection [{}]", e1.getMessage()); + + ret = false; + } + } else { + ret = false; + } + } + + return ret; + } + + /** + * Method to close the websocket connection + */ + public void closeWebSocketConnection() { + socketMonitor.interrupt(); + + QueuedThreadPool localThreadPool = jettyThreadPool; + + if (localThreadPool != null) { + try { + localThreadPool.stop(); + } catch (Exception e1) { + logger.debug("Error by closing Websocket connection [{}]", e1.getMessage()); + } + jettyThreadPool = null; + } + + WebSocketClient localWebSocketClient = websocketClient; + + if (localWebSocketClient != null) { + try { + localWebSocketClient.stop(); + } catch (Exception e2) { + logger.debug("Error by closing Websocket connection [{}]", e2.getMessage()); + } + websocketClient = null; + } + } + + /** + * Method to open the websocket connection + */ + public boolean openWebSocketConnection() { + boolean ret = false; + + QueuedThreadPool localThreadPool = jettyThreadPool; + + if (localThreadPool == null) { + jettyThreadPool = new QueuedThreadPool(); + + localThreadPool = jettyThreadPool; + + if (localThreadPool != null) { + localThreadPool.setName(FreeAtHomeBridgeHandler.class.getSimpleName()); + localThreadPool.setDaemon(true); + localThreadPool.setStopTimeout(0); + + ret = true; + } + } + + WebSocketClient localWebSocketClient = websocketClient; + + if (localWebSocketClient == null) { + websocketClient = new WebSocketClient(httpClient); + + localWebSocketClient = websocketClient; + + if (localWebSocketClient != null) { + localWebSocketClient.setExecutor(jettyThreadPool); + + socketMonitor.start(); + + ret = true; + } else { + ret = false; + } + } else { + ret = true; + } + + return ret; + } + + /** + * Method to initialize the bridge + */ + @Override + public void initialize() { + httpConnectionOK.set(false); + + // load configuration + FreeAtHomeBridgeHandlerConfiguration locConfig = getConfigAs(FreeAtHomeBridgeHandlerConfiguration.class); + + ipAddress = locConfig.ipAddress; + if (ipAddress.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.ip-address-missing"); + return; + } + + password = locConfig.password; + if (password.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.password-missing"); + return; + } + + username = locConfig.username; + if (username.isBlank()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.username-missing"); + return; + } + + // build base URL + baseUrl = "http://" + ipAddress + "/fhapi/v1/api"; + + updateStatus(ThingStatus.UNKNOWN); + + scheduler.execute(() -> { + boolean thingReachable = true; + + // Open Http connection + if (!openHttpConnection()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.http-wrongpass-or-ip"); + + thingReachable = false; + } + + // Open the websocket connection for immediate status updates + if (!openWebSocketConnection()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.not-able-open-websocketconnection"); + + thingReachable = false; + } + + if (thingReachable) { + updateStatus(ThingStatus.ONLINE); + } + }); + } + + /** + * Method to dispose + */ + @Override + public void dispose() { + // let run out the thread + socketMonitor.interrupt(); + + closeWebSocketConnection(); + } + + /** + * Thread that maintains connection via Websocket. + */ + private class FreeAtHomeWebsocketMonitorThread extends Thread { + + // initial delay to initiate connection + private AtomicInteger reconnectDelay = new AtomicInteger(); + + public FreeAtHomeWebsocketMonitorThread() { + } + + @Override + public void run() { + // set initial connect delay to 0 + reconnectDelay.set(0); + + try { + while (!isInterrupted()) { + if (httpConnectionOK.get()) { + if (connectSession()) { + while (isSocketConnectionAlive()) { + TimeUnit.SECONDS.sleep(BRIDGE_WEBSOCKET_KEEPALIVE); + + logger.debug("Sending keep-alive message {}", System.currentTimeMillis()); + sendWebsocketKeepAliveMessage("keep-alive"); + } + } + logger.debug("Socket connection closed"); + reconnectDelay.set(BRIDGE_WEBSOCKET_RECONNECT_DELAY); + } else { + TimeUnit.SECONDS.sleep(BRIDGE_WEBSOCKET_RECONNECT_DELAY); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.general-websocket-issue"); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.websocket-keep-alive-error"); + } + } + + private boolean connectSession() throws InterruptedException { + int delay = reconnectDelay.get(); + + if (delay > 0) { + logger.debug("Delaying (re)connect request by {} seconds.", reconnectDelay); + TimeUnit.SECONDS.sleep(delay); + } + + logger.debug("Server connecting to websocket"); + + if (!connectWebsocketSession()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.general-websocket-issue"); + + reconnectDelay.set(BRIDGE_WEBSOCKET_RECONNECT_DELAY); + + return false; + } + + if (websocketSession == null) { + lock.lock(); + try { + websocketSessionEstablished.await(); + } finally { + lock.unlock(); + } + } + + return true; + } + } + + /** + * Send keep-alive message to SysAp + */ + public void sendWebsocketKeepAliveMessage(String message) throws IOException { + Session localSession = websocketSession; + + if (localSession != null) { + localSession.getRemote().sendString(message); + } + } + + /** + * Get socket alive state + * + * @throws InterruptedException + */ + public boolean isSocketConnectionAlive() throws InterruptedException { + Session localSession = websocketSession; + + return (localSession != null) ? localSession.isOpen() : false; + } + + /** + * Socket closed. Report the state + */ + @Override + public void onWebSocketClose(int statusCode, @Nullable String reason) { + websocketSession = null; + logger.debug("Socket Closed: [ {} ] {}", statusCode, reason); + } + + /** + * Socket connected. store the session for later use + */ + @Override + public void onWebSocketConnect(@Nullable Session session) { + Session localSession = session; + + if (localSession != null) { + websocketSession = localSession; + + localSession.setIdleTimeout(-1); + + logger.debug("Socket Connected - Timeout {} - sesson: {}", localSession.getIdleTimeout(), session); + } else { + logger.debug("Socket Connected - Timeout (invalid) - sesson: (invalid)"); + } + + lock.lock(); + try { + websocketSessionEstablished.signal(); + } finally { + lock.unlock(); + } + } + + /** + * Error caused. Report the state + */ + @Override + public void onWebSocketError(@Nullable Throwable cause) { + websocketSession = null; + + if (cause != null) { + logger.debug("Socket Error: {}", cause.getLocalizedMessage()); + } else { + logger.debug("Socket Error: unknown"); + } + } + + /** + * Binary message received. It shall not happen with the free@home SysAp + */ + @Override + @NonNullByDefault({}) + public void onWebSocketBinary(byte[] payload, int offset, int len) { + logger.debug("Binary message received via websocket"); + } + + /** + * Text message received. Processing will be started + */ + @Override + public void onWebSocketText(@Nullable String message) { + if (message != null) { + if (message.toLowerCase(Locale.US).contains("bye")) { + Session localSession = websocketSession; + + if (localSession != null) { + localSession.close(StatusCode.NORMAL, "Thanks"); + } + + logger.debug("Websocket connection closed: {} ", message); + } else { + logger.debug("Received websocket text: {} ", message); + + setDatapointOnWebsocketFeedback(message); + + markDeviceRemovedOnWebsocketFeedback(message); + } + } else { + logger.debug("Invalid message string"); + } + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceHandler.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceHandler.java new file mode 100644 index 0000000000000..b5a6d64b502cd --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceHandler.java @@ -0,0 +1,635 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.handler; + +import java.math.BigDecimal; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.freeathomesystem.internal.configuration.FreeAtHomeDeviceHandlerConfiguration; +import org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDatapoint; +import org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDatapointGroup; +import org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDeviceChannel; +import org.openhab.binding.freeathomesystem.internal.datamodel.FreeAtHomeDeviceDescription; +import org.openhab.binding.freeathomesystem.internal.type.FreeAtHomeChannelTypeProvider; +import org.openhab.binding.freeathomesystem.internal.util.FreeAtHomeGeneralException; +import org.openhab.binding.freeathomesystem.internal.util.FreeAtHomeHttpCommunicationException; +import org.openhab.binding.freeathomesystem.internal.util.UidUtils; +import org.openhab.binding.freeathomesystem.internal.valuestateconverter.ValueStateConverter; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.binding.builder.ThingBuilder; +import org.openhab.core.thing.type.AutoUpdatePolicy; +import org.openhab.core.thing.type.ChannelKind; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeBuilder; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link FreeAtHomeDeviceHandler} is responsible for handling the generic free@home device main communication + * and thing updates + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeDeviceHandler extends BaseThingHandler implements FreeAtHomeDeviceStateListener { + + private static final String CHANNEL_URI = "channel-type:freeathomesystem:config"; + + private final Logger logger = LoggerFactory.getLogger(FreeAtHomeDeviceHandler.class); + private FreeAtHomeDeviceDescription device = new FreeAtHomeDeviceDescription(); + private FreeAtHomeChannelTypeProvider channelTypeProvider; + private TranslationProvider i18nProvider; + private Locale locale; + private Bundle bundle; + + private Map mapChannelUID = new HashMap(); + private Map mapEventToChannelUID = new HashMap(); + + public FreeAtHomeDeviceHandler(Thing thing, FreeAtHomeChannelTypeProvider channelTypeProvider, + TranslationProvider i18nProvider, LocaleProvider localeProvider) { + super(thing); + + this.channelTypeProvider = channelTypeProvider; + this.i18nProvider = i18nProvider; + this.bundle = FrameworkUtil.getBundle(getClass()); + this.locale = localeProvider.getLocale(); + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + + scheduler.execute(() -> { + FreeAtHomeDeviceHandlerConfiguration config = getConfigAs(FreeAtHomeDeviceHandlerConfiguration.class); + + Bridge bridge = this.getBridge(); + String locDeviceId = config.deviceId; + + if (bridge != null) { + ThingHandler handler = bridge.getHandler(); + + if (handler instanceof FreeAtHomeBridgeHandler bridgeHandler) { + if (!locDeviceId.isBlank()) { + try { + device = bridgeHandler.getFreeatHomeDeviceDescription(locDeviceId); + + updateChannels(); + } catch (FreeAtHomeHttpCommunicationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + } catch (FreeAtHomeGeneralException e) { + logger.debug("General error in the binding - during initialization {}", + device.getDeviceId()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.general-binding-error"); + } + + // register device for status updates + bridgeHandler.registerDeviceStateListener(device.getDeviceId(), this); + + updateStatus(ThingStatus.ONLINE); + + logger.debug("Device created - device id: {}", device.getDeviceId()); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.invalid-deviceconfig"); + + logger.debug("Device cannot be created: device ID is null!"); + } + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "@text/conf-error.bridge-not-configured"); + + logger.debug("Device cannot be created: no bridge is configured!"); + return; + } + }); + } + + @Override + public void dispose() { + Bridge bridge = this.getBridge(); + + // Unregister device and specific channel for event based state updated + if (bridge != null) { + ThingHandler handler = bridge.getHandler(); + + if (handler instanceof FreeAtHomeBridgeHandler bridgeHandler) { + bridgeHandler.unregisterDeviceStateListener(device.getDeviceId()); + } + } + + // Remove mapping tables + mapChannelUID.clear(); + + mapEventToChannelUID.clear(); + + logger.debug("Device removed - device id: {}", device.getDeviceId()); + } + + private void handleRefreshCommand(FreeAtHomeBridgeHandler freeAtHomeBridge, FreeAtHomeDatapointGroup dpg, + ChannelUID channelUID) { + String valueStr = "0"; + String channelID = "ch000"; + String datapointID = "0"; + + // Check whether it is a INPUT only datapoint group + + if (dpg.getDirection() == FreeAtHomeDatapointGroup.DatapointGroupDirection.INPUT) { + FreeAtHomeDatapoint datapoint = dpg.getInputDatapoint(); + + if (datapoint != null) { + channelID = datapoint.channelId; + datapointID = datapoint.getDatapointId(); + } + } else { + FreeAtHomeDatapoint datapoint = dpg.getOutputDatapoint(); + + if (datapoint != null) { + channelID = datapoint.channelId; + datapointID = datapoint.getDatapointId(); + } + } + + try { + valueStr = freeAtHomeBridge.getDatapoint(device.getDeviceId(), channelID, datapointID); + + ValueStateConverter vsc = dpg.getValueStateConverter(); + + updateState(channelUID, vsc.convertToState(valueStr)); + } catch (FreeAtHomeHttpCommunicationException e) { + logger.debug("Communication error during refresh command {} - at channel {} - Error string {}", + device.getDeviceId(), channelUID.getAsString(), e.getMessage()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + } catch (FreeAtHomeGeneralException e) { + logger.debug("General error in the binding - during REFRESH command {} - at channel {} - Error string {}", + device.getDeviceId(), channelUID.getAsString(), e.getMessage()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.general-binding-error"); + } + } + + private void handleSetCommand(FreeAtHomeBridgeHandler freeAtHomeBridge, FreeAtHomeDatapointGroup dpg, + ChannelUID channelUID, Command command) { + State state = null; + String valueString = "0"; + + // initial error handling. look for the data point group validity + FreeAtHomeDatapoint datapoint = dpg.getInputDatapoint(); + + if (datapoint == null) { + logger.debug("Invalid parameter in handleSetCommand - DeviceId - {} - at channel {}", device.getDeviceId(), + channelUID.getAsString()); + + return; + } + + try { + ValueStateConverter vsc = dpg.getValueStateConverter(); + + if (command instanceof StopMoveType) { + valueString = "0"; + } else { + state = ((State) command); + valueString = vsc.convertToValueString(state); + } + + freeAtHomeBridge.setDatapoint(device.getDeviceId(), datapoint.channelId, datapoint.getDatapointId(), + valueString); + + if (!device.isScene()) { + if (state != null) { + updateState(channelUID, state); + } else { + updateState(channelUID, new StringType("STOP")); + } + } + } catch (FreeAtHomeHttpCommunicationException e) { + logger.debug( + "Communication error during set command {} - at channel {} - full command {} - Error string {}", + device.getDeviceId(), channelUID.getAsString(), command.toFullString(), e.getMessage()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + } catch (FreeAtHomeGeneralException e) { + logger.debug("General error in the binding - during SET command {} - at channel {} - Error string {}", + device.getDeviceId(), channelUID.getAsString(), e.getMessage()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.general-binding-error"); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + FreeAtHomeBridgeHandler freeAtHomeBridge = null; + + Bridge bridge = this.getBridge(); + + if (bridge != null) { + ThingHandler handler = bridge.getHandler(); + + if (handler instanceof FreeAtHomeBridgeHandler bridgeHandler) { + freeAtHomeBridge = bridgeHandler; + } + } + + if (freeAtHomeBridge != null) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "@text/conf-error.invalid-bridge"); + return; + } + + FreeAtHomeDatapointGroup dpg = mapChannelUID.get(channelUID); + + // is the datapointgroup invalid + if (dpg == null) { + logger.debug("Handle command for device (but invalid datapointgroup) {} - at channel {} - full command {}", + device.getDeviceId(), channelUID.getAsString(), command.toFullString()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.invalid-deviceconfig"); + } else { + if (command instanceof RefreshType) { + handleRefreshCommand(freeAtHomeBridge, dpg, channelUID); + } else { + handleSetCommand(freeAtHomeBridge, dpg, channelUID, command); + } + + logger.debug("Handle command for device {} - at channel {} - full command {}", device.getDeviceId(), + channelUID.getAsString(), command.toFullString()); + } + } + + public void onDeviceRemoved() { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.GONE); + } + + public void onDeviceStateChanged(String event, String valueString) { + // Get the channle UID belonging to this event + ChannelUID channelUID = mapEventToChannelUID.get(event); + + try { + if (channelUID != null) { + // get the value State Converter for the channel + FreeAtHomeDatapointGroup dpg = mapChannelUID.get(channelUID); + + if (dpg != null) { + State state; + state = dpg.getValueStateConverter().convertToState(valueString); + + // Handle state change + handleEventBasedUpdate(channelUID, state); + + // if it is virtual device, give a feedback to free@home also + if (isThingHandlesVirtualDevice()) { + feedbackForVirtualDevice(channelUID, valueString); + } + } + } + } catch (FreeAtHomeGeneralException e) { + logger.debug("General error in the binding during onDeviceStateChange"); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.general-binding-error"); + } + } + + private void handleEventBasedUpdate(ChannelUID channelUID, State state) { + this.updateState(channelUID, state); + } + + private void feedbackForVirtualDevice(ChannelUID channelUID, String valueString) { + FreeAtHomeBridgeHandler freeAtHomeBridge = null; + + FreeAtHomeDatapointGroup dpg = mapChannelUID.get(channelUID); + + Bridge bridge = this.getBridge(); + + if (bridge != null) { + ThingHandler handler = bridge.getHandler(); + + if (handler instanceof FreeAtHomeBridgeHandler bridgeHandler) { + freeAtHomeBridge = bridgeHandler; + } + } + + if (freeAtHomeBridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "@text/gen-error.no-bridge-avail"); + return; + } + + if (dpg == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.datapointgroup-invalid"); + return; + } + + FreeAtHomeDatapoint inputDatapoint = dpg.getInputDatapoint(); + + if (inputDatapoint == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/conf-error.inputdatapoint-invalid"); + return; + } + + if ((dpg.getDirection() != FreeAtHomeDatapointGroup.DatapointGroupDirection.INPUT) + || (dpg.getDirection() != FreeAtHomeDatapointGroup.DatapointGroupDirection.INPUTOUTPUT)) { + logger.debug("Handle feedback for virtual device {} - at channel {} - but wrong config", + device.getDeviceId(), channelUID.getAsString()); + } + + try { + freeAtHomeBridge.setDatapoint(device.getDeviceId(), inputDatapoint.channelId, + inputDatapoint.getDatapointId(), valueString); + + updateStatus(ThingStatus.ONLINE); + + logger.debug("Handle feedback for virtual device {} - at channel {} - value {}", device.getDeviceId(), + channelUID.getAsString(), valueString); + } catch (FreeAtHomeHttpCommunicationException e) { + logger.debug("Communication error during set command {} - at channel {} - value {} - Error string {}", + device.getDeviceId(), channelUID.getAsString(), valueString, e.getMessage()); + + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.not-able-open-httpconnection"); + } + } + + public ChannelTypeUID createChannelTypeForDatapointgroup(FreeAtHomeDatapointGroup dpg, + ChannelTypeUID channelTypeUID) throws FreeAtHomeGeneralException { + StateDescriptionFragmentBuilder stateFragment = StateDescriptionFragmentBuilder.create(); + + stateFragment.withReadOnly(dpg.isReadOnly()); + stateFragment.withPattern(dpg.getTypePattern()); + + if (dpg.isDecimal() || dpg.isInteger()) { + BigDecimal min = new BigDecimal(dpg.getMin()); + BigDecimal max = new BigDecimal(dpg.getMax()); + stateFragment.withMinimum(min).withMaximum(max); + } + + try { + URI configDescriptionUriChannel = new URI(CHANNEL_URI); + + ChannelTypeBuilder channelTypeBuilder = ChannelTypeBuilder + .state(channelTypeUID, + String.format("%s-%s-%s-%s", dpg.getLabel(), dpg.getOpenHabItemType(), + dpg.getOpenHabCategory(), "type"), + dpg.getOpenHabItemType()) + .withCategory(dpg.getOpenHabCategory()).withStateDescriptionFragment(stateFragment.build()); + + ChannelType channelType = channelTypeBuilder.isAdvanced(false) + .withConfigDescriptionURI(configDescriptionUriChannel) + .withDescription(String.format("Type for channel - %s ", dpg.getLabel())).build(); + + channelTypeProvider.addChannelType(channelType); + + logger.debug("Channel type created {} - label: {} - category: {}", channelTypeUID.getAsString(), + dpg.getLabel(), dpg.getOpenHabCategory()); + } catch (URISyntaxException e) { + logger.debug("Channel config URI cannot created for datapoint - datapoint group: {}", dpg.getLabel()); + } + + return channelTypeUID; + } + + public void updateChannels() throws FreeAtHomeGeneralException { + // define update policy + AutoUpdatePolicy policy = AutoUpdatePolicy.DEFAULT; + + if (device.isScene()) { + policy = AutoUpdatePolicy.VETO; + } + + // Initialize channels + List thingChannels = new ArrayList<>(this.getThing().getChannels()); + + if (thingChannels.isEmpty()) { + ThingBuilder thingBuilder = editThing(); + + ThingUID thingUID = thing.getUID(); + + for (int i = 0; i < device.getNumberOfChannels(); i++) { + FreeAtHomeDeviceChannel channel = device.getChannel(i); + + for (int j = 0; j < channel.getNumberOfDatapointGroup(); j++) { + FreeAtHomeDatapointGroup dpg = channel.getDatapointGroup(j); + Map channelProps = new HashMap<>(); + + FreeAtHomeDatapoint inputDatapoint = dpg.getInputDatapoint(); + FreeAtHomeDatapoint outputDatapoint = dpg.getOutputDatapoint(); + + if (inputDatapoint != null) { + channelProps.put("input", inputDatapoint.getDatapointId()); + } + + if (outputDatapoint != null) { + channelProps.put("output", outputDatapoint.getDatapointId()); + } + + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dpg.getValueType(), + dpg.isReadOnly()); + + if (channelTypeProvider.getChannelType(channelTypeUID, null) == null) { + channelTypeUID = createChannelTypeForDatapointgroup(dpg, channelTypeUID); + } + + ChannelUID channelUID = new ChannelUID(thingUID, channel.getChannelId(), + dpg.getLabel().substring(4)); + + String channelLabel = String.format("%s", + i18nProvider.getText(bundle, dpg.getLabel(), "-", locale)); + + String channelDescription = String.format("(%s) %s", channel.getChannelLabel(), + i18nProvider.getText(bundle, dpg.getDescription(), "-", locale)); + + Channel thingChannel = ChannelBuilder.create(channelUID) + .withAcceptedItemType(dpg.getOpenHabItemType()).withKind(ChannelKind.STATE) + .withProperties(channelProps).withLabel(capitalizeWordsInLabel(channelLabel)) + .withDescription(channelDescription).withType(channelTypeUID).withAutoUpdatePolicy(policy) + .build(); + thingChannels.add(thingChannel); + + logger.debug("Thing channel created - device: {} - channelUID: {} - channel label: {}", + device.getDeviceId() + device.getDeviceLabel(), channelUID.getAsString(), channelLabel); + + // in case of output channel, register it for updates + if (outputDatapoint != null) { + String eventDatapointID = new String(device.getDeviceId() + "/" + channel.getChannelId() + "/" + + outputDatapoint.getDatapointId()); + + mapEventToChannelUID.put(eventDatapointID, channelUID); + } + + // add the datapoint group to the mapping channel + mapChannelUID.put(channelUID, dpg); + + if (dpg.getInputDatapoint() == null) { + logger.debug( + "Thing channel registered - device: {} - channelUID: {} - channel label: {} - category: {}", + device.getDeviceId() + device.getDeviceLabel(), channelUID.getAsString(), + dpg.getLabel(), dpg.getOpenHabCategory()); + } else { + logger.debug( + "Thing channel registered - device: {} - channelUID: {} - channel label: {} - category: {}", + device.getDeviceId() + device.getDeviceLabel(), channelUID.getAsString(), + dpg.getLabel(), dpg.getOpenHabCategory()); + } + } + + thingBuilder.withChannels(thingChannels); + + updateThing(thingBuilder.build()); + } + } else { + reloadChannelTypes(); + } + + thingChannels.forEach(channel -> { + if (isLinked(channel.getUID())) { + channelLinked(channel.getUID()); + } + }); + } + + private void reloadChannelTypes() throws FreeAtHomeGeneralException { + Bridge bridge = this.getBridge(); + + ThingUID thingUID = thing.getUID(); + + try { + if (bridge != null) { + ThingHandler handler = bridge.getHandler(); + + if (handler instanceof FreeAtHomeBridgeHandler bridgeHandler) { + device = bridgeHandler.getFreeatHomeDeviceDescription(device.getDeviceId()); + } + } + } catch (FreeAtHomeHttpCommunicationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + } + + for (int i = 0; i < device.getNumberOfChannels(); i++) { + FreeAtHomeDeviceChannel channel = device.getChannel(i); + + for (int j = 0; j < channel.getNumberOfDatapointGroup(); j++) { + FreeAtHomeDatapointGroup dpg = channel.getDatapointGroup(j); + + ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dpg.getValueType(), dpg.isReadOnly()); + + if (channelTypeProvider.getChannelType(channelTypeUID, null) == null) { + channelTypeUID = createChannelTypeForDatapointgroup(dpg, channelTypeUID); + } + + ChannelUID channelUID = new ChannelUID(thingUID, channel.getChannelId()); + + FreeAtHomeDatapoint outputDatapoint = dpg.getOutputDatapoint(); + + // in case of output channel, register it for updates + if (outputDatapoint != null) { + String eventDatapointID = new String(device.getDeviceId() + "/" + channel.getChannelId() + "/" + + outputDatapoint.getDatapointId()); + + mapEventToChannelUID.put(eventDatapointID, channelUID); + } + + // add the datapoint group to the mapping channel + mapChannelUID.put(channelUID, dpg); + + logger.debug("Thing channelType reloaded - Device: {} - channelTypeUID: {}", + device.getDeviceId() + device.getDeviceLabel(), channelTypeUID.getAsString()); + } + } + } + + public void removeChannels() { + Bridge bridge = this.getBridge(); + + try { + if (bridge != null) { + ThingHandler handler = bridge.getHandler(); + + if (handler instanceof FreeAtHomeBridgeHandler bridgeHandler) { + device = bridgeHandler.getFreeatHomeDeviceDescription(device.getDeviceId()); + } + } + } catch (FreeAtHomeHttpCommunicationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/comm-error.error-in-sysap-com"); + } + + mapChannelUID.clear(); + + mapEventToChannelUID.clear(); + } + + private String capitalizeWordsInLabel(String label) { + // splliting up words using split function + String[] words = label.split(" "); + + for (int i = 0; i < words.length; i++) { + + // taking letter individually from sentences + String firstLetter = words[i].substring(0, 1); + String restOfWord = words[i].substring(1); + + // making first letter uppercase using toUpperCase function + firstLetter = firstLetter.toUpperCase(); + words[i] = firstLetter + restOfWord; + } + + // joining the words together to make a sentence + return String.join(" ", words); + } + + private boolean isThingHandlesVirtualDevice() { + return device.isVirtual(); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceStateListener.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceStateListener.java new file mode 100644 index 0000000000000..6a5d886c922e7 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/handler/FreeAtHomeDeviceStateListener.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link FreeAtHomeDeviceStateListener} is responsible for response the device state changes from SysAp + * and thing updates + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public interface FreeAtHomeDeviceStateListener { + void onDeviceRemoved(); + + void onDeviceStateChanged(String event, String valueString); +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProvider.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProvider.java new file mode 100644 index 0000000000000..b89e68d8a4114 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProvider.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeProvider; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public interface FreeAtHomeChannelTypeProvider extends ChannelTypeProvider { + + public void addChannelType(ChannelType channelType); +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProviderImpl.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProviderImpl.java new file mode 100644 index 0000000000000..a9cdf31876d50 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeChannelTypeProviderImpl.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.type; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.type.ChannelType; +import org.openhab.core.thing.type.ChannelTypeProvider; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.osgi.service.component.annotations.Component; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ +@Component(service = { FreeAtHomeChannelTypeProvider.class, ChannelTypeProvider.class }) +@NonNullByDefault +public class FreeAtHomeChannelTypeProviderImpl implements FreeAtHomeChannelTypeProvider { + + private final Map channelTypesByUID = new HashMap<>(); + + @Override + public Collection getChannelTypes(@Nullable Locale locale) { + Collection result = new ArrayList<>(); + + for (ChannelTypeUID uid : channelTypesByUID.keySet()) { + ChannelType channelType = channelTypesByUID.get(uid); + + if (channelType != null) { + result.add(channelType); + } + } + + return result; + } + + @Override + public @Nullable ChannelType getChannelType(@Nullable ChannelTypeUID channelTypeUID, @Nullable Locale locale) { + return channelTypesByUID.get(channelTypeUID); + } + + @Override + public void addChannelType(@Nullable ChannelType channelType) { + if (channelType != null) { + channelTypesByUID.put(channelType.getUID(), channelType); + } + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProvider.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProvider.java new file mode 100644 index 0000000000000..c2c1193faf2cc --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProvider.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.binding.ThingTypeProvider; +import org.openhab.core.thing.type.ThingType; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public interface FreeAtHomeThingTypeProvider extends ThingTypeProvider { + + /** + * Adds the ThingType to this provider. + */ + public void addThingType(ThingType thingType); +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProviderImpl.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProviderImpl.java new file mode 100644 index 0000000000000..807933a5b6e28 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/type/FreeAtHomeThingTypeProviderImpl.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.type; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.ThingTypeProvider; +import org.openhab.core.thing.type.ThingType; +import org.osgi.service.component.annotations.Component; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ + +@Component(service = { FreeAtHomeThingTypeProvider.class, ThingTypeProvider.class }) +@NonNullByDefault +public class FreeAtHomeThingTypeProviderImpl implements FreeAtHomeThingTypeProvider { + private final Map thingTypesByUID = new HashMap<>(); + + @Override + public Collection getThingTypes(@Nullable Locale locale) { + Map copy = new HashMap<>(thingTypesByUID); + return copy.values(); + } + + @Override + public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) { + return thingTypesByUID.get(thingTypeUID); + } + + @Override + public void addThingType(@Nullable ThingType thingType) { + if (thingType != null) { + thingTypesByUID.put(thingType.getUID(), thingType); + } + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FidTranslationUtils.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FidTranslationUtils.java new file mode 100644 index 0000000000000..96ff6bcaa3b10 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FidTranslationUtils.java @@ -0,0 +1,362 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.util; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link FidTranslationUtils} having constant values for json parsing + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FidTranslationUtils { + + public static final int FID_UNKNOWN = 0xFFFFAAFF; // Unknown + + // free@home constants + public static final int FID_SWITCH_SENSOR = 0x0000; // Control element + public static final int FID_DIMMING_SENSOR = 0x0001; // Dimming sensor + public static final int FID_BLIND_SENSOR = 0x0003; // Blind sensor + public static final int FID_STAIRCASE_LIGHT_SENSOR = 0x0004; // Stairwell light sensor + public static final int FID_FORCE_ON_OFF_SENSOR = 0x0005; // Force On/Off sensor + public static final int FID_SCENE_SENSOR = 0x0006; // Scene sensor + public static final int FID_SWITCH_ACTUATOR = 0x0007; // Switch actuator + public static final int FID_SHUTTER_ACTUATOR = 0x0009; // Blind actuator + public static final int FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITH_FAN = 0x000A; // Room temperature controller + // with fan speed level + public static final int FID_ROOM_TEMPERATURE_CONTROLLER_SLAVE = 0x000B; // Room temperature controller extension + // unit + public static final int FID_WIND_ALARM_SENSOR = 0x000C; // Wind Alarm + public static final int FID_FROST_ALARM_SENSOR = 0x000D; // Frost Alarm + public static final int FID_RAIN_ALARM_SENSOR = 0x000E; // Rain Alarm + public static final int FID_WINDOW_DOOR_SENSOR = 0x000F; // Window sensor + public static final int FID_MOVEMENT_DETECTOR = 0x0011; // Movement Detector + public static final int FID_DIMMING_ACTUATOR = 0x0012; // Dim actuator + public static final int FID_RADIATOR_ACTUATOR = 0x0014; // Radiator + public static final int FID_UNDERFLOOR_HEATING = 0x0015; // Underfloor heating + public static final int FID_FAN_COIL = 0x0016; // Fan Coil + public static final int FID_TWO_LEVEL_CONTROLLER = 0x0017; // Two-level controller + public static final int FID_DES_DOOR_OPENER_ACTUATOR = 0x001A; // Door opener + public static final int FID_PROXY = 0x001B; // Proxy + public static final int FID_DES_LEVEL_CALL_ACTUATOR = 0x001D; // Door Map.entry System Call Level Actuator + public static final int FID_DES_LEVEL_CALL_SENSOR = 0x001E; // Door Map.entry System Call Level Sensor + public static final int FID_DES_DOOR_RINGING_SENSOR = 0x001F; // Door call + public static final int FID_DES_AUTOMATIC_DOOR_OPENER_ACTUATOR = 0x0020; // Automatic door opener + public static final int FID_DES_LIGHT_SWITCH_ACTUATOR = 0x0021; // Corridor light + public static final int FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN = 0x0023; // Room temperature controller + public static final int FID_COOLING_ACTUATOR = 0x0024; // Cooling mode + public static final int FID_HEATING_ACTUATOR = 0x0027; // Heating mode + public static final int FID_FORCE_UP_DOWN_SENSOR = 0x0028; // Force-position blind + public static final int FID_HEATING_COOLING_ACTUATOR = 0x0029; // Auto. heating/cooling mode + public static final int FID_HEATING_COOLING_SENSOR = 0x002A; // Switchover heating/cooling + public static final int FID_DES_DEVICE_SETTINGS = 0x002B; // Device settings + public static final int FID_RGB_W_ACTUATOR = 0x002E; // Dim actuator + public static final int FID_RGB_ACTUATOR = 0x002F; // Dim actuator + public static final int FID_PANEL_SWITCH_SENSOR = 0x0030; // Control element + public static final int FID_PANEL_DIMMING_SENSOR = 0x0031; // Dimming sensor + public static final int FID_PANEL_BLIND_SENSOR = 0x0033; // Blind sensor + public static final int FID_PANEL_STAIRCASE_LIGHT_SENSOR = 0x0034; // Stairwell light sensor + public static final int FID_PANEL_FORCE_ON_OFF_SENSOR = 0x0035; // Force On/Off sensor + public static final int FID_PANEL_FORCE_UP_DOWN_SENSOR = 0x0036; // Force-position blind + public static final int FID_PANEL_SCENE_SENSOR = 0x0037; // Scene sensor + public static final int FID_PANEL_ROOM_TEMPERATURE_CONTROLLER_SLAVE = 0x0038; // Room temperature controller + // extension unit + public static final int FID_PANEL_FAN_COIL_SENSOR = 0x0039; // Fan coil sensor + public static final int FID_PANEL_RGB_CT_SENSOR = 0x003A; // RGB + warm white/cold white sensor + public static final int FID_PANEL_RGB_SENSOR = 0x003B; // RGB sensor + public static final int FID_PANEL_CT_SENSOR = 0x003C; // Warm white/cold white sensor + public static final int FID_ADDITIONAL_HEATING_ACTUATOR = 0x003D; // Add. stage for heating mode + public static final int FID_RADIATOR_ACTUATOR_MASTER = 0x003E; // Radiator thermostate + public static final int FID_RADIATOR_ACTUATOR_SLAVE = 0x003F; // Room temperature controller extension unit + public static final int FID_BRIGHTNESS_SENSOR = 0x0041; // Brightness sensor + public static final int FID_RAIN_SENSOR = 0x0042; // Rain sensor + public static final int FID_TEMPERATURE_SENSOR = 0x0043; // Temperature sensor + public static final int FID_WIND_SENSOR = 0x0044; // Wind sensor + public static final int FID_TRIGGER = 0x0045; // Trigger + public static final int FID_FCA_2_PIPE_HEATING = 0x0047; // Heating mode + public static final int FID_FCA_2_PIPE_COOLING = 0x0048; // Cooling mode + public static final int FID_FCA_2_PIPE_HEATING_COOLING = 0x0049; // Auto. heating/cooling mode + public static final int FID_FCA_4_PIPE_HEATING_AND_COOLING = 0x004A; // Two valves for heating and cooling + public static final int FID_WINDOW_DOOR_ACTUATOR = 0x004B; // Window/Door + public static final int FID_INVERTER_INFO = 0x004E; // ABC + public static final int FID_METER_INFO = 0x004F; // ABD + public static final int FID_BATTERY_INFO = 0x0050; // ACD + public static final int FID_PANEL_TIMER_PROGRAM_SWITCH_SENSOR = 0x0051; // Timer program switch sensor + public static final int FID_DOMUSTECH_ZONE = 0x0055; // Zone + public static final int FID_CENTRAL_HEATING_ACTUATOR = 0x0056; // Central heating actuator + public static final int FID_CENTRAL_COOLING_ACTUATOR = 0x0057; // Central cooling actuator + public static final int FID_HOUSE_KEEPING = 0x0059; // Housekeeping + public static final int FID_MEDIA_PLAYER = 0x005A; // Media Player + public static final int FID_PANEL_ROOM_TEMPERATURE_CONTROLLER_SLAVE_FOR_BATTERY_DEVICE = 0x005B; // Panel Room + // Temperature + // Controller Slave + // For Battery + // Device + public static final int FID_PANEL_MEDIA_PLAYER_SENSOR = 0x0060; // Media Player Sensor + public static final int FID_BLIND_ACTUATOR = 0x0061; // Roller blind actuator + public static final int FID_ATTIC_WINDOW_ACTUATOR = 0x0062; // Attic window actuator + public static final int FID_AWNING_ACTUATOR = 0x0063; // Awning actuator + public static final int FID_WINDOW_DOOR_POSITION_SENSOR = 0x0064; // WindowDoor Position Sensor + public static final int FID_WINDOW_DOOR_POSITION_ACTUATOR = 0x0065; // Window/Door position + public static final int FID_MEDIA_PLAYBACK_CONTROL_SENSOR = 0x0066; // Media playback control sensor + public static final int FID_MEDIA_VOLUME_SENSOR = 0x0067; // Media volume sensor + public static final int FID_DISHWASHER = 0x0068; // Dishwasher + public static final int FID_LAUNDRY = 0x0069; // Laundry + public static final int FID_DRYER = 0x006A; // Dryer + public static final int FID_OVEN = 0x006B; // Oven + public static final int FID_FRIDGE = 0x006C; // Fridge + public static final int FID_FREEZER = 0x006D; // Freezer + public static final int FID_HOOD = 0x006E; // Hood + public static final int FID_COFFEE_MACHINE = 0x006F; // Coffee machine + public static final int FID_FRIDGE_FREEZER = 0x0070; // Fridge/Freezer + public static final int FID_TIMER_PROGRAM_OR_ALERT_SWITCH_SENSOR = 0x0071; // Timer program switch sensor + public static final int FID_CEILING_FAN_ACTUATOR = 0x0073; // Ceiling fan actuator + public static final int FID_CEILING_FAN_SENSOR = 0x0074; // Ceiling fan sensor + public static final int FID_SPLIT_UNIT_GATEWAY = 0x0075; // Room temperature controller with fan speed level + public static final int FID_ZONE = 0x0076; // Zone + public static final int FID_24H_ZONE = 0x0077; // Safety + public static final int FID_EXTERNAL_IR_SENSOR_BX80 = 0x0078; // External IR Sensor BX80 + public static final int FID_EXTERNAL_IR_SENSOR_VXI = 0x0079; // External IR Sensor VXI + public static final int FID_EXTERNAL_IR_SENSOR_MINI = 0x007A; // External IR Sensor Mini + public static final int FID_EXTERNAL_IR_SENSOR_HIGH_ALTITUDE = 0x007B; // External IR Sensor High Altitude + public static final int FID_EXTERNAL_IR_SENSOR_CURTAIN = 0x007C; // External IR Sensor Curtain + public static final int FID_SMOKE_DETECTOR = 0x007D; // Smoke Detector + public static final int FID_CARBON_MONOXIDE_SENSOR = 0x007E; // Carbon Monoxide Sensor + public static final int FID_METHANE_DETECTOR = 0x007F; // Methane Detector + public static final int FID_GAS_SENSOR_LPG = 0x0080; // Gas Sensor LPG + public static final int FID_FLOOD_DETECTION = 0x0081; // Flood Detection + public static final int FID_DOMUS_CENTRAL_UNIT_NEXTGEN = 0x0082; // secure@home Central Unit + public static final int FID_THERMOSTAT = 0x0083; // Thermostat + public static final int FID_PANEL_DOMUS_ZONE_SENSOR = 0x0084; // secure@home Zone Sensor + public static final int FID_THERMOSTAT_SLAVE = 0x0085; // Slave thermostat + public static final int FID_DOMUS_SECURE_INTEGRATION = 0x0086; // secure@home Integration Logic + public static final int FID_ADDITIONAL_COOLING_ACTUATOR = 0x0087; // Add. stage for cooling mode + public static final int FID_TWO_LEVEL_HEATING_ACTUATOR = 0x0088; // Two Level Heating Actuator + public static final int FID_TWO_LEVEL_COOLING_ACTUATOR = 0x0089; // Two Level Cooling Actuator + public static final int FID_GLOBAL_ZONE = 0x008E; // Zone + public static final int FID_VOLUME_UP_SENSOR = 0x008F; // Volume up + public static final int FID_VOLUME_DOWN_SENSOR = 0x0090; // Volume down + public static final int FID_PLAY_PAUSE_SENSOR = 0x0091; // Play/pause + public static final int FID_NEXT_FAVORITE_SENSOR = 0x0092; // Next favorite + public static final int FID_NEXT_SONG_SENSOR = 0x0093; // Next song + public static final int FID_PREVIOUS_SONG_SENSOR = 0x0094; // Previous song + public static final int FID_HOME_APPLIANCE_SENSOR = 0x0095; // Home appliance sensor + public static final int FID_HEAT_SENSOR = 0x0096; // Heat sensor + public static final int FID_ZONE_SWITCHING = 0x0097; // Zone switching + public static final int FID_SECURE_AT_HOME_FUNCTION = 0x0098; // Button function + public static final int FID_COMPLEX_CONFIGURATION = 0x0099; // Advanced configuration + public static final int FID_DOMUS_CENTRAL_UNIT_BASIC = 0x009A; // secure@home Central Unit Basic + public static final int FID_DOMUS_REPEATER = 0x009B; // Repeater + public static final int FID_DOMUS_SCENE_TRIGGER = 0x009C; // Remote scene control + public static final int FID_DOMUSWINDOWCONTACT = 0x009D; // Window sensor + public static final int FID_DOMUSMOVEMENTDETECTOR = 0x009E; // Movement Detector + public static final int FID_DOMUSCURTAINDETECTOR = 0x009F; // External IR Sensor Curtain + public static final int FID_DOMUSSMOKEDETECTOR = 0x00A0; // Smoke Detector + public static final int FID_DOMUSFLOODDETECTOR = 0x00A1; // Flood Detection + public static final int FID_PANEL_SUG_SENSOR = 0x00A3; // Sensor for air-conditioning unit + public static final int FID_TWO_LEVEL_HEATING_COOLING_ACTUATOR = 0x00A4; // Two-point controller for heating or + // cooling + public static final int FID_PANEL_THERMOSTAT_CONTROLLER_SLAVE = 0x00A5; // Slave thermostat + public static final int FID_WALLBOX = 0x00A6; // Wallbox + public static final int FID_PANEL_WALLBOX = 0x00A7; // Wallbox + public static final int FID_DOOR_LOCK_CONTROL = 0x00A8; // Door lock control + public static final int FID_VRV_GATEWAY = 0x00AA; // Room temperature controller with fan speed level + + public static final int FID_SCENE_TRIGGER = 0x4800; // Scene trigger + public static final int FID_RULE_SWITCH = 0x4A00; // Scene trigger + + // FID added based on tests + public static final int FID_AIRQUALITYSENSOR_PRESSURE = 0x0E017; + public static final int FID_AIRQUALITYSENSOR_CO2 = 0x0E018; + public static final int FID_AIRQUALITYSENSOR_CO = 0x0E019; + public static final int FID_AIRQUALITYSENSOR_NO2 = 0x0E01A; + public static final int FID_AIRQUALITYSENSOR_O3 = 0x0E01B; + public static final int FID_AIRQUALITYSENSOR_PM10 = 0x0E01C; + public static final int FID_AIRQUALITYSENSOR_PM25 = 0x0E01D; + public static final int FID_AIRQUALITYSENSOR_VOC = 0x0E01E; + public static final int FID_AIRQUALITYSENSOR_HUMIDITY = 0x0B03F; + + public static final int FID_MOVEMENT_DETECTOR_FLEX = 0x1090; + public static final int FID_DIMMING_ACTUATOR_FLEX = 0x1810; + + private static final Map MAP_FUNCTION_ID = Map.ofEntries(Map.entry("0x0000", "fid-control-element"), // FID_SWITCH_SENSOR + Map.entry("0x0001", "fid-dimming-sensor"), // FID_DIMMING_SENSOR + Map.entry("0x0003", "fid-blind-sensor"), // FID_BLIND_SENSOR + Map.entry("0x0004", "fid-stairwell-light-sensor"), // FID_STAIRCASE_LIGHT_SENSOR + Map.entry("0x0005", "fid-force-on/off-sensor"), // FID_FORCE_ON_OFF_SENSOR + Map.entry("0x0006", "fid-scene-sensor"), // FID_SCENE_SENSOR + Map.entry("0x0007", "fid-switch-actuator"), // FID_SWITCH_ACTUATOR + Map.entry("0x0009", "fid-blind-actuator"), // FID_SHUTTER_ACTUATOR + Map.entry("0x000A", "fid-room-temperature-controller-with-fan-speed-level"), // FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITH_FAN + Map.entry("0x000B", "fid-room-temperature-controller-extension-unit"), // FID_ROOM_TEMPERATURE_CONTROLLER_SLAVE + Map.entry("0x000C", "fid-wind-alarm"), // FID_WIND_ALARM_SENSOR + Map.entry("0x000D", "fid-frost-alarm"), // FID_FROST_ALARM_SENSOR + Map.entry("0x000E", "fid-rain-alarm"), // FID_RAIN_ALARM_SENSOR + Map.entry("0x000F", "fid-window-sensor"), // FID_WINDOW_DOOR_SENSOR + Map.entry("0x0011", "fid-movement-detector"), // FID_MOVEMENT_DETECTOR + Map.entry("0x0012", "fid-dim-actuator"), // FID_DIMMING_ACTUATOR + Map.entry("0x0014", "fid-radiator"), // FID_RADIATOR_ACTUATOR + Map.entry("0x0015", "fid-underfloor-heating"), // FID_UNDERFLOOR_HEATING + Map.entry("0x0016", "fid-fan-coil"), // FID_FAN_COIL + Map.entry("0x0017", "fid-two-level-controller"), // FID_TWO_LEVEL_CONTROLLER + Map.entry("0x001A", "fid-door-opener"), // FID_DES_DOOR_OPENER_ACTUATOR + Map.entry("0x001B", "fid-proxy"), // FID_PROXY + Map.entry("0x001D", "fid-door-map.entry-system-call-level-actuator"), // FID_DES_LEVEL_CALL_ACTUATOR + Map.entry("0x001E", "fid-door-map.entry-system-call-level-sensor"), // FID_DES_LEVEL_CALL_SENSOR + Map.entry("0x001F", "fid-door-call"), // FID_DES_DOOR_RINGING_SENSOR + Map.entry("0x0020", "fid-automatic-door-opener"), // FID_DES_AUTOMATIC_DOOR_OPENER_ACTUATOR + Map.entry("0x0021", "fid-corridor-light"), // FID_DES_LIGHT_SWITCH_ACTUATOR + Map.entry("0x0023", "fid-room-temperature-controller"), // FID_ROOM_TEMPERATURE_CONTROLLER_MASTER_WITHOUT_FAN + Map.entry("0x0024", "fid-cooling-mode"), // FID_COOLING_ACTUATOR + Map.entry("0x0027", "fid-heating-mode"), // FID_HEATING_ACTUATOR + Map.entry("0x0028", "fid-force-position-blind"), // FID_FORCE_UP_DOWN_SENSOR + Map.entry("0x0029", "fid-auto.-heating/cooling-mode"), // FID_HEATING_COOLING_ACTUATOR + Map.entry("0x002A", "fid-switchover-heating/cooling"), // FID_HEATING_COOLING_SENSOR + Map.entry("0x002B", "fid-device-settings"), // FID_DES_DEVICE_SETTINGS + Map.entry("0x002E", "fid-dim-actuator"), // FID_RGB_W_ACTUATOR + Map.entry("0x002F", "fid-dim-actuator"), // FID_RGB_ACTUATOR + Map.entry("0x0030", "fid-control-element"), // FID_PANEL_SWITCH_SENSOR + Map.entry("0x0031", "fid-dimming-sensor"), // FID_PANEL_DIMMING_SENSOR + Map.entry("0x0033", "fid-blind-sensor"), // FID_PANEL_BLIND_SENSOR + Map.entry("0x0034", "fid-stairwell-light-sensor"), // FID_PANEL_STAIRCASE_LIGHT_SENSOR + Map.entry("0x0035", "fid-force-on/off-sensor"), // FID_PANEL_FORCE_ON_OFF_SENSOR + Map.entry("0x0036", "fid-force-position-blind"), // FID_PANEL_FORCE_UP_DOWN_SENSOR + Map.entry("0x0037", "fid-scene-sensor"), // FID_PANEL_SCENE_SENSOR + Map.entry("0x0038", "fid-room-temperature-controller-extension-unit"), // FID_PANEL_ROOM_TEMPERATURE_CONTROLLER_SLAVE + Map.entry("0x0039", "fid-fan-coil-sensor"), // FID_PANEL_FAN_COIL_SENSOR + Map.entry("0x003A", "fid-rgb-+-warm-white/cold-white-sensor"), // FID_PANEL_RGB_CT_SENSOR + Map.entry("0x003B", "fid-rgb-sensor"), // FID_PANEL_RGB_SENSOR + Map.entry("0x003C", "fid-warm-white/cold-white-sensor"), // FID_PANEL_CT_SENSOR + Map.entry("0x003D", "fid-add.-stage-for-heating-mode"), // FID_ADDITIONAL_HEATING_ACTUATOR + Map.entry("0x003E", "fid-radiator-thermostate"), // FID_RADIATOR_ACTUATOR_MASTER + Map.entry("0x003F", "fid-room-temperature-controller-extension-unit"), // FID_RADIATOR_ACTUATOR_SLAVE + Map.entry("0x0041", "fid-brightness-sensor"), // FID_BRIGHTNESS_SENSOR + Map.entry("0x0042", "fid-rain-sensor"), // FID_RAIN_SENSOR + Map.entry("0x0043", "fid-temperature-sensor"), // FID_TEMPERATURE_SENSOR + Map.entry("0x0044", "fid-wind-sensor"), // FID_WIND_SENSOR + Map.entry("0x0045", "fid-trigger"), // FID_TRIGGER + Map.entry("0x0047", "fid-heating-mode"), // FID_FCA_2_PIPE_HEATING + Map.entry("0x0048", "fid-cooling-mode"), // FID_FCA_2_PIPE_COOLING + Map.entry("0x0049", "fid-auto.-heating/cooling-mode"), // FID_FCA_2_PIPE_HEATING_COOLING + Map.entry("0x004A", "fid-two-valves-for-heating-and-cooling"), // FID_FCA_4_PIPE_HEATING_AND_COOLING + Map.entry("0x004B", "fid-window/door"), // FID_WINDOW_DOOR_ACTUATOR + Map.entry("0x004E", "fid-abc"), // FID_INVERTER_INFO + Map.entry("0x004F", "fid-abd"), // FID_METER_INFO + Map.entry("0x0050", "fid-acd"), // FID_BATTERY_INFO + Map.entry("0x0051", "fid-timer-program-switch-sensor"), // FID_PANEL_TIMER_PROGRAM_SWITCH_SENSOR + Map.entry("0x0055", "fid-zone"), // FID_DOMUSTECH_ZONE + Map.entry("0x0056", "fid-central-heating-actuator"), // FID_CENTRAL_HEATING_ACTUATOR + Map.entry("0x0057", "fid-central-cooling-actuator"), // FID_CENTRAL_COOLING_ACTUATOR + Map.entry("0x0059", "fid-housekeeping"), // FID_HOUSE_KEEPING + Map.entry("0x005A", "fid-media-player"), // FID_MEDIA_PLAYER + Map.entry("0x005B", "fid-panel-room-temperature-controller-slave-for-battery-device"), // FID_PANEL_ROOM_TEMPERATURE_CONTROLLER_SLAVE_FOR_BATTERY_DEVICE + Map.entry("0x0060", "fid-media-player-sensor"), // FID_PANEL_MEDIA_PLAYER_SENSOR + Map.entry("0x0061", "fid-roller-blind-actuator"), // FID_BLIND_ACTUATOR + Map.entry("0x0062", "fid-attic-window-actuator"), // FID_ATTIC_WINDOW_ACTUATOR + Map.entry("0x0063", "fid-awning-actuator"), // FID_AWNING_ACTUATOR + Map.entry("0x0064", "fid-windowdoor-position-sensor"), // FID_WINDOW_DOOR_POSITION_SENSOR + Map.entry("0x0065", "fid-window/door-position"), // FID_WINDOW_DOOR_POSITION_ACTUATOR + Map.entry("0x0066", "fid-media-playback-control-sensor"), // FID_MEDIA_PLAYBACK_CONTROL_SENSOR + Map.entry("0x0067", "fid-media-volume-sensor"), // FID_MEDIA_VOLUME_SENSOR + Map.entry("0x0068", "fid-dishwasher"), // FID_DISHWASHER + Map.entry("0x0069", "fid-laundry"), // FID_LAUNDRY + Map.entry("0x006A", "fid-dryer"), // FID_DRYER + Map.entry("0x006B", "fid-oven"), // FID_OVEN + Map.entry("0x006C", "fid-fridge"), // FID_FRIDGE + Map.entry("0x006D", "fid-freezer"), // FID_FREEZER + Map.entry("0x006E", "fid-hood"), // FID_HOOD + Map.entry("0x006F", "fid-coffee-machine"), // FID_COFFEE_MACHINE + Map.entry("0x0070", "fid-fridge/freezer"), // FID_FRIDGE_FREEZER + Map.entry("0x0071", "fid-timer-program-switch-sensor"), // FID_TIMER_PROGRAM_OR_ALERT_SWITCH_SENSOR + Map.entry("0x0073", "fid-ceiling-fan-actuator"), // FID_CEILING_FAN_ACTUATOR + Map.entry("0x0074", "fid-ceiling-fan-sensor"), // FID_CEILING_FAN_SENSOR + Map.entry("0x0075", "fid-room-temperature-controller-with-fan-speed-level"), // FID_SPLIT_UNIT_GATEWAY + Map.entry("0x0076", "fid-zone"), // FID_ZONE + Map.entry("0x0077", "fid-safety"), // FID_24H_ZONE + Map.entry("0x0078", "fid-external-ir-sensor-bx80"), // FID_EXTERNAL_IR_SENSOR_BX80 + Map.entry("0x0079", "fid-external-ir-sensor-vxi"), // FID_EXTERNAL_IR_SENSOR_VXI + Map.entry("0x007A", "fid-external-ir-sensor-mini"), // FID_EXTERNAL_IR_SENSOR_MINI + Map.entry("0x007B", "fid-external-ir-sensor-high-altitude"), // FID_EXTERNAL_IR_SENSOR_HIGH_ALTITUDE + Map.entry("0x007C", "fid-external-ir-sensor-curtain"), // FID_EXTERNAL_IR_SENSOR_CURTAIN + Map.entry("0x007D", "fid-smoke-detector"), // FID_SMOKE_DETECTOR + Map.entry("0x007E", "fid-carbon-monoxide-sensor"), // FID_CARBON_MONOXIDE_SENSOR + Map.entry("0x007F", "fid-methane-detector"), // FID_METHANE_DETECTOR + Map.entry("0x0080", "fid-gas-sensor-lpg"), // FID_GAS_SENSOR_LPG + Map.entry("0x0081", "fid-flood-detection"), // FID_FLOOD_DETECTION + Map.entry("0x0082", "fid-secure@home-central-unit"), // FID_DOMUS_CENTRAL_UNIT_NEXTGEN + Map.entry("0x0083", "fid-thermostat"), // FID_THERMOSTAT + Map.entry("0x0084", "fid-secure@home-zone-sensor"), // FID_PANEL_DOMUS_ZONE_SENSOR + Map.entry("0x0085", "fid-slave-thermostat"), // FID_THERMOSTAT_SLAVE + Map.entry("0x0086", "fid-secure@home-integration-logic"), // FID_DOMUS_SECURE_INTEGRATION + Map.entry("0x0087", "fid-add.-stage-for-cooling-mode"), // FID_ADDITIONAL_COOLING_ACTUATOR + Map.entry("0x0088", "fid-two-level-heating-actuator"), // FID_TWO_LEVEL_HEATING_ACTUATOR + Map.entry("0x0089", "fid-two-level-cooling-actuator"), // FID_TWO_LEVEL_COOLING_ACTUATOR + Map.entry("0x008E", "fid-zone"), // FID_GLOBAL_ZONE + Map.entry("0x008F", "fid-volume-up"), // FID_VOLUME_UP_SENSOR + Map.entry("0x0090", "fid-volume-down"), // FID_VOLUME_DOWN_SENSOR + Map.entry("0x0091", "fid-play/pause"), // FID_PLAY_PAUSE_SENSOR + Map.entry("0x0092", "fid-next-favorite"), // FID_NEXT_FAVORITE_SENSOR + Map.entry("0x0093", "fid-next-song"), // FID_NEXT_SONG_SENSOR + Map.entry("0x0094", "fid-previous-song"), // FID_PREVIOUS_SONG_SENSOR + Map.entry("0x0095", "fid-home-appliance-sensor"), // FID_HOME_APPLIANCE_SENSOR + Map.entry("0x0096", "fid-heat-sensor"), // FID_HEAT_SENSOR + Map.entry("0x0097", "fid-zone-switching"), // FID_ZONE_SWITCHING + Map.entry("0x0098", "fid-button-function"), // FID_SECURE_AT_HOME_FUNCTION + Map.entry("0x0099", "fid-advanced-configuration"), // FID_COMPLEX_CONFIGURATION + Map.entry("0x009A", "fid-secure@home-central-unit-basic"), // FID_DOMUS_CENTRAL_UNIT_BASIC + Map.entry("0x009B", "fid-repeater"), // FID_DOMUS_REPEATER + Map.entry("0x009C", "fid-remote-scene-control"), // FID_DOMUS_SCENE_TRIGGER + Map.entry("0x009D", "fid-window-sensor"), // FID_DOMUSWINDOWCONTACT + Map.entry("0x009E", "fid-movement-detector"), // FID_DOMUSMOVEMENTDETECTOR + Map.entry("0x009F", "fid-external-ir-sensor-curtain"), // FID_DOMUSCURTAINDETECTOR + Map.entry("0x00A0", "fid-smoke-detector"), // FID_DOMUSSMOKEDETECTOR + Map.entry("0x00A1", "fid-flood-detection"), // FID_DOMUSFLOODDETECTOR + Map.entry("0x00A3", "fid-sensor-for-air-conditioning-unit"), // FID_PANEL_SUG_SENSOR + Map.entry("0x00A4", "fid-two-point-controller-for-heating-or-cooling"), // FID_TWO_LEVEL_HEATING_COOLING_ACTUATOR + Map.entry("0x00A5", "fid-slave-thermostat"), // FID_PANEL_THERMOSTAT_CONTROLLER_SLAVE + Map.entry("0x00A6", "fid-wallbox"), // FID_WALLBOX + Map.entry("0x00A7", "fid-wallbox"), // FID_PANEL_WALLBOX + Map.entry("0x00A8", "fid-door-lock-control"), // FID_DOOR_LOCK_CONTROL + Map.entry("0x00AA", "fid-room-temperature-controller-with-fan-speed-level"), // FID_VRV_GATEWAY + Map.entry("0x4800", "fid-scene-trigger"), // FID_SCENE_TRIGGER + Map.entry("0x4A00", "fid-rule-switch"), // FID_RULE_SWITCH + Map.entry("0xE017", "fid-air-quality-sensor-pressure"), // FID_AIRQUALITYSENSOR_PRESSURE + Map.entry("0xE018", "fid-air-quality-sensor-co2"), // FID_AIRQUALITYSENSOR_CO2 + Map.entry("0xE019", "fid-air-quality-sensor-co"), // FID_AIRQUALITYSENSOR_CO + Map.entry("0xE01A", "fid-air-quality-sensor-no2"), // FID_AIRQUALITYSENSOR_NO2 + Map.entry("0xE01B", "fid-air-quality-sensor-o3"), // FID_AIRQUALITYSENSOR_O3 + Map.entry("0xE01C", "fid-air-quality-sensor-pm10"), // FID_AIRQUALITYSENSOR_PM10 + Map.entry("0xE01D", "fid-air-quality-sensor-pm25"), // FID_AIRQUALITYSENSOR_PM25 + Map.entry("0xE01E", "fid-air-quality-sensor-voc"), // FID_AIRQUALITYSENSOR_VOC + Map.entry("0xB03F", "fid-air-quality-sensor-humidity"), // FID_AIRQUALITYSENSOR_HUMIDITY + Map.entry("0x1090", "fid-movement-detector-flex"), // FID_MOVEMENT_DETECTOR_FLEX + Map.entry("0x1810", "fid-dim-actuator-flex") // FID_SWITCH_ACTUATOR_FLEX + ); + + @Nullable + public static String getFunctionIdText(String Key) throws FreeAtHomeGeneralException { + String result = MAP_FUNCTION_ID.get(Key); + + if (result != null) { + return MAP_FUNCTION_ID.get(Key); + } else { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "FID is not in the translation table", Key)); + } + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeGeneralException.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeGeneralException.java new file mode 100644 index 0000000000000..c490db2f69104 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeGeneralException.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.util; + +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link FreeAtHomeGeneralException} is responsible for handling general exceptions in the free@home binding + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeGeneralException extends Exception { + private static final long serialVersionUID = -835448863173642860L; + private String errorMessage = "Unknown_Exception"; + private int errorCode = 0; + + public FreeAtHomeGeneralException(int errorCode, String message) { + super(message); + + this.errorMessage = message; + this.errorCode = errorCode; + } + + public @Nullable String getMessage() { + return this.errorMessage; + } + + public int getErrorCode() { + return this.errorCode; + } + + @Override + public String toString() { + return "FreeAtHomeHttpCommunicationException [errorMessage=" + errorMessage + ", errorCode=" + errorCode + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(errorCode, errorMessage); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + FreeAtHomeGeneralException other = (FreeAtHomeGeneralException) obj; + + return errorCode == other.errorCode && Objects.equals(errorMessage, other.errorMessage); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeHttpCommunicationException.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeHttpCommunicationException.java new file mode 100644 index 0000000000000..cfea1397f8999 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/FreeAtHomeHttpCommunicationException.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.util; + +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link FreeAtHomeHttpCommunicationException} is responsible for handling the communication exception to the SysAp + * via HTTP link + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class FreeAtHomeHttpCommunicationException extends Exception { + private static final long serialVersionUID = -817364286035448863L; + private String errorMessage = "Unknown_Exception"; + private int errorCode; + + public FreeAtHomeHttpCommunicationException(int errorCode, String message) { + super(message); + + this.errorMessage = message; + this.errorCode = errorCode; + } + + public @Nullable String getMessage() { + return this.errorMessage; + } + + public int getErrorCode() { + return this.errorCode; + } + + @Override + public String toString() { + return "FreeAtHomeHttpCommunicationException [errorMessage=" + errorMessage + ", errorCode=" + errorCode + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(errorCode, errorMessage); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + FreeAtHomeHttpCommunicationException other = (FreeAtHomeHttpCommunicationException) obj; + + return errorCode == other.errorCode && Objects.equals(errorMessage, other.errorMessage); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PIdContainerClass.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PIdContainerClass.java new file mode 100644 index 0000000000000..e534a11dca3a5 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PIdContainerClass.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link PIdContainerClass} is a helper class for pairing IDs + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class PIdContainerClass { + String valueType; + String category; + int min; + int max; + String label; + String description; + + PIdContainerClass(String pValueType, String pCategory, String pMin, String pMax, String pLabel, + String pDescription) { + this.valueType = pValueType; + + this.category = pCategory; + + if (pMax.isEmpty()) { + this.min = 0; + } else { + this.min = Integer.parseInt(pMin); + } + + if (pMax.isEmpty()) { + this.max = 100; + } else { + this.max = Integer.parseInt(pMax); + } + + this.label = pLabel; + + this.description = pDescription; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PidTranslationUtils.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PidTranslationUtils.java new file mode 100644 index 0000000000000..3e5b05fd9b0fe --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/PidTranslationUtils.java @@ -0,0 +1,829 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.util; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link PidTranslationUtils} supporting the translation from pairing IDs into openHAB types + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class PidTranslationUtils { + public static final String PID_VALUETYPE_UNKNOWN = "unknown"; + public static final String PID_VALUETYPE_BOOLEAN = "boolean"; + public static final String PID_VALUETYPE_DECIMAL = "decimal"; + public static final String PID_VALUETYPE_INTEGER = "integer"; + public static final String PID_VALUETYPE_STRING = "string"; + public static final String PID_VALUETYPE_SHUTTERMOVEMENT = "shuttermovement"; + public static final String PID_VALUETYPE_ENUM = "enum"; + + public static final String CATEGORY_UNDEFINED = "-"; + public static final String CATEGORY_BATTERY = "Battery"; + public static final String CATEGORY_ALARM = "Alarm"; + public static final String CATEGORY_HUMIDITY = "Humidity"; + public static final String CATEGORY_TEMPERATURE = "Temperature"; + public static final String CATEGORY_MOTION = "Motion"; + public static final String CATEGORY_PRESSURE = "Pressure"; + public static final String CATEGORY_SMOKE = "Smoke"; + public static final String CATEGORY_WATER = "Water"; + public static final String CATEGORY_WIND = "Wind"; + public static final String CATEGORY_RAIN = "Rain"; + public static final String CATEGORY_ENERGY = "Energy"; + public static final String CATEGORY_BLINDS = "Blinds"; + public static final String CATEGORY_CONTACT = "Contact"; + public static final String CATEGORY_SWITCH = "Switch"; + + private static final Map MAP_PAIRING_ID = Map.ofEntries( + Map.entry("0x0001", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "0", "1", "pid-switch-on-off", + "pid-switch-on-off-text")), + Map.entry("0x0002", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-timed-start-stop", + "pid-timed-start-stop-text")), + Map.entry("0x0003", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-force-position", + "pid-force-position-text")), + Map.entry("0x0004", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-scene-control", + "pid-scene-control-text")), + Map.entry("0x0006", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_MOTION, "", "", + "pid-movement-under-consideration-of-brightness", + "pid-movement-under-consideration-of-brightness-text")), + Map.entry("0x0007", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_MOTION, "", "", "pid-presence", + "pid-presence-text")), + Map.entry("0x0010", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-relative-set-value", + "pid-relative-set-value-text")), + Map.entry("0x0011", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-absolute-set-value", + "pid-absolute-set-value-text")), + Map.entry("0x0012", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-night", + "pid-night-text")), + Map.entry("0x0013", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-invalid-string-id", + "pid-invalid-string-id-text")), + Map.entry("0x0015", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-rgb-color", + "pid-rgb-color-text")), + Map.entry("0x0016", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-color-temperature", + "pid-color-temperature-text")), + Map.entry("0x0017", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-hsv", + "pid-hsv-text")), + Map.entry("0x0018", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-hue", + "pid-hue-text")), + Map.entry("0x0019", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-saturation", + "pid-saturation-text")), + Map.entry("0x0020", + new PIdContainerClass(PID_VALUETYPE_SHUTTERMOVEMENT, CATEGORY_BLINDS, "", "", "pid-move-up-down", + "pid-move-up-down-text")), + Map.entry("0x0021", + new PIdContainerClass(PID_VALUETYPE_SHUTTERMOVEMENT, CATEGORY_BLINDS, "", "", "pid-adjust-up-down", + "pid-adjust-up-down-text")), + Map.entry("0x0023", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_BLINDS, "0", "100", + "pid-set-absolute-position-blinds", "pid-set-absolute-position-blinds-text")), + Map.entry("0x0024", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_BLINDS, "0", "100", + "pid-set-absolute-position-slats", "pid-set-absolute-position-slats-text")), + Map.entry("0x0025", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_WIND, "", "", "pid-wind-alarm", + "pid-wind-alarm-text")), + Map.entry("0x0026", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_TEMPERATURE, "", "", "pid-frost-alarm", + "pid-frost-alarm-text")), + Map.entry("0x0027", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_RAIN, "", "", "pid-rain-alarm", + "pid-rain-alarm-text")), + Map.entry("0x0028", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_UNDEFINED, "", "", "pid-force-position-blind", + "pid-force-position-blind-text")), + Map.entry("0x0029", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-window-door-position", + "pid-window-door-position-text")), + Map.entry("0x0030", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-actuating-value-heating", "pid-actuating-value-heating-text")), + Map.entry("0x0031", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-fan-level-heating", + "pid-fan-level-heating-text")), + Map.entry("0x0032", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-actuating-value-cooling", "pid-actuating-value-cooling-text")), + Map.entry("0x0033", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "7", "30", + "pid-set-value-temperature", "pid-set-value-temperature-text")), + Map.entry("0x0034", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "7", "30", + "pid-relative-set-point-temperature", "pid-relative-set-point-temperature-text")), + Map.entry("0x0035", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-window-door", + "pid-window-door-text")), + Map.entry("0x0036", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", "pid-status-indication", + "pid-status-indication-text")), + Map.entry("0x0037", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", + "pid-fan-manual-heating-on-off", "pid-fan-manual-heating-on-off-text")), + Map.entry("0x0038", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-controller-on-off", + "pid-controller-on-off-text")), + Map.entry("0x0039", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "7", "30", + "pid-relative-set-point-request", "pid-relative-set-point-request-text")), + Map.entry("0x003A", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-eco-mode-on-off-request", + "pid-eco-mode-on-off-request-text")), + Map.entry("0x003B", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "7", "30", + "pid-comfort-temperature", "pid-comfort-temperature-text")), + Map.entry("0x0040", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-fan-level-request", + "pid-fan-level-request-text")), + Map.entry("0x0041", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", + "pid-fan-manual-on-off-request", "pid-fan-manual-on-off-request-text")), + Map.entry("0x0042", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", + "pid-controller-on-off-request", "pid-controller-on-off-request-text")), + Map.entry("0x0044", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-eco-mode-on-off-request", + "pid-eco-mode-on-off-request-text")), + Map.entry("0x0100", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-info-on-off", + "pid-info-on-off-text")), + Map.entry("0x0101", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_UNDEFINED, "", "", "pid-force-position-info", + "pid-force-position-info-text")), + Map.entry("0x0105", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_UNDEFINED, "", "", "pid-sysap-infoonoff", + "pid-sysap-infoonoff-text")), + Map.entry("0x0106", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-sysap-infoforce", + "pid-sysap-infoforce-text")), + Map.entry("0x0110", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-info-actual-dimming-value", "pid-info-actual-dimming-value-text")), + Map.entry("0x0111", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-info-error", + "pid-info-error-text")), + Map.entry("0x0115", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-sysap-infocurrentdimmingvalue", "pid-sysap-infocurrentdimmingvalue-text")), + Map.entry("0x0116", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-sysap-infoerror", + "pid-sysap-infoerror-text")), + Map.entry("0x0118", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-color-temperature", "pid-info-color-temperature-text")), + Map.entry("0x011A", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-sysap-info-color-temperature", "pid-sysap-info-color-temperature-text")), + Map.entry("0x011B", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-hsv", + "pid-info-hsv-text")), + Map.entry("0x011C", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-sysap-info-hsv", + "pid-sysap-info-hsv-text")), + Map.entry("0x011D", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-color-mode", + "pid-info-color-mode-text")), + Map.entry("0x011E", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-sysap-info-color-mode", "pid-sysap-info-color-mode-text")), + Map.entry("0x0120", + new PIdContainerClass(PID_VALUETYPE_SHUTTERMOVEMENT, CATEGORY_BLINDS, "", "", + "pid-info-move-up-down", "pid-info-move-up-down-text")), + Map.entry("0x0121", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_BLINDS, "", "", + "pid-current-absolute-position-blinds-percentage", + "pid-current-absolute-position-blinds-percentage-text")), + Map.entry("0x0122", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-current-absolute-position-slats-percentage", + "pid-current-absolute-position-slats-percentage-text")), + Map.entry("0x0125", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-sysap-infomoveupdown", + "pid-sysap-infomoveupdown-text")), + Map.entry("0x0126", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-sysap-infocurrentabsoluteblindspercentage", + "pid-sysap-infocurrentabsoluteblindspercentage-text")), + Map.entry("0x0127", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-sysap-infocurrentabsoluteslatspercentage", + "pid-sysap-infocurrentabsoluteslatspercentage-text")), + Map.entry("0x0130", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "7", "30", + "pid-measured-temperature", "pid-measured-temperature-text")), + Map.entry("0x0131", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-value-heating", + "pid-info-value-heating-text")), + Map.entry("0x0132", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", "pid-info-value-cooling", + "pid-info-value-cooling-text")), + Map.entry("0x0135", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-switchover-heating-cooling", "pid-switchover-heating-cooling-text")), + Map.entry("0x0136", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-actuating-fan-stage-heating", "pid-actuating-fan-stage-heating-text")), + Map.entry("0x0140", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "7", "30", + "pid-absolute-setpoint-temperature", "pid-absolute-setpoint-temperature-text")), + Map.entry("0x0141", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-additional-heating-value-info", "pid-additional-heating-value-info-text")), + Map.entry("0x0142", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-additional-cooling-value-info", "pid-additional-cooling-value-info-text")), + Map.entry("0x0143", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-control-value-additional-heating", "pid-control-value-additional-heating-text")), + Map.entry("0x0144", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-control-value-additional-cooling", "pid-control-value-additional-cooling-text")), + Map.entry("0x0145", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-actuating-fan-stage-heating", "pid-info-actuating-fan-stage-heating-text")), + Map.entry("0x0146", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-actuating-fan-manual-on-off-heating", + "pid-info-actuating-fan-manual-on-off-heating-text")), + Map.entry("0x0147", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-actuating-fan-stage-cooling", "pid-actuating-fan-stage-cooling-text")), + Map.entry("0x0149", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-info-fan-stage-cooling", "pid-info-fan-stage-cooling-text")), + Map.entry("0x014A", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_TEMPERATURE, "", "", + "pid-info-fan-manual-on-off-cooling", "pid-info-fan-manual-on-off-cooling-text")), + Map.entry("0x014B", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", "pid-heating-active", + "pid-heating-active-text")), + Map.entry("0x014C", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", "pid-cooling-active", + "pid-cooling-active-text")), + Map.entry("0x014D", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", "pid-heating-demand", + "pid-heating-demand-text")), + Map.entry("0x014E", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", "pid-cooling-demand", + "pid-cooling-demand-text")), + Map.entry("0x014F", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-heating-demand-feedback-signal", "pid-heating-demand-feedback-signal-text")), + Map.entry("0x0150", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_TEMPERATURE, "", "", + "pid-cooling-demand-feedback-signal", "pid-cooling-demand-feedback-signal-text")), + Map.entry("0x0151", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_HUMIDITY, "", "", "pid-humidity", + "pid-humidity-text")), + Map.entry("0x0152", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-aux-on-off-request", + "pid-aux-on-off-request-text")), + Map.entry("0x0153", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-aux-on-off-response", + "pid-aux-on-off-response-text")), + Map.entry("0x0154", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-heating-on-off-request", + "pid-heating-on-off-request-text")), + Map.entry("0x0155", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-cooling-on-off-request", + "pid-cooling-on-off-request-text")), + Map.entry("0x0156", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-operation-mode", + "pid-operation-mode-text")), + Map.entry("0x0157", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-swing-h-v", + "pid-swing-h-v-text")), + Map.entry("0x0158", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-supported-features", + "pid-supported-features-text")), + Map.entry("0x0159", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-extended-status-indication", "pid-extended-status-indication-text")), + Map.entry("0x015A", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-extended-status-indication", "pid-extended-status-indication-text")), + Map.entry("0x015B", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-aux-heating-on-off-request", "pid-aux-heating-on-off-request-text")), + Map.entry("0x015C", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-emergency-heating-on-off-request", "pid-emergency-heating-on-off-request-text")), + Map.entry("0x0160", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-relative-fan-speed-control", "pid-relative-fan-speed-control-text")), + Map.entry("0x0161", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-absolute-fan-speed-control", "pid-absolute-fan-speed-control-text")), + Map.entry("0x0162", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-absolute-fan-speed", "pid-info-absolute-fan-speed-text")), + Map.entry("0x0163", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-sysap-infoactualfanspeed", "pid-sysap-infoactualfanspeed-text")), + Map.entry("0x01A0", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-notification-flags", + "pid-notification-flags-text")), + Map.entry("0x0280", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-power-rc", + "pid-power-rc-text")), + Map.entry("0x0281", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-power-rh", + "pid-power-rh-text")), + Map.entry("0x0282", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-proximity-status", + "pid-proximity-status-text")), + Map.entry("0x0290", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-brightness-sensor", + "pid-brightness-sensor-text")), + Map.entry("0x0291", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-last-touch", + "pid-last-touch-text")), + Map.entry("0x0292", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-led-backlighting-night-mode", "pid-led-backlighting-night-mode-text")), + Map.entry("0x02C0", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-locator-beep", + "pid-locator-beep-text")), + Map.entry("0x02C1", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-switch-test-alarm", + "pid-switch-test-alarm-text")), + Map.entry("0x02C3", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-fire-alarm-active", + "pid-fire-alarm-active-text")), + Map.entry("0x0400", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_TEMPERATURE, "", "", + "pid-outside-temperature", "pid-outside-temperature-text")), + Map.entry("0x0401", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_WIND, "", "", "pid-wind-force", + "pid-wind-force-text")), + Map.entry("0x0402", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_UNDEFINED, "", "", "pid-brightness-alarm", + "pid-brightness-alarm-text")), + Map.entry("0x0403", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_UNDEFINED, "", "", "pid-lux-value", + "pid-lux-value-text")), + Map.entry("0x0404", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_WIND, "", "", "pid-wind-speed", + "pid-wind-speed-text")), + Map.entry("0x0405", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_RAIN, "", "", "pid-rain-detection", + "pid-rain-detection-text")), + Map.entry("0x0406", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_RAIN, "", "", "pid-rain-sensor-frequency", + "pid-rain-sensor-frequency-text")), + Map.entry("0x0440", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-play", + "pid-play-text")), + Map.entry("0x0441", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-pause", + "pid-pause-text")), + Map.entry("0x0442", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-next", + "pid-next-text")), + Map.entry("0x0443", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-previous", + "pid-previous-text")), + Map.entry("0x0444", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-play-mode", + "pid-play-mode-text")), + Map.entry("0x0445", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-mute", + "pid-mute-text")), + Map.entry("0x0446", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-relative-volume-control", "pid-relative-volume-control-text")), + Map.entry("0x0447", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-absolute-volume-control", "pid-absolute-volume-control-text")), + Map.entry("0x0448", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-group-membership", + "pid-group-membership-text")), + Map.entry("0x0449", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-play-favorite", + "pid-play-favorite-text")), + Map.entry("0x044A", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-play-next-favorite", + "pid-play-next-favorite-text")), + Map.entry("0x0460", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-playback-status", + "pid-playback-status-text")), + Map.entry("0x0461", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-current-item-metadata-info", "pid-current-item-metadata-info-text")), + Map.entry("0x0462", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-mute", + "pid-info-mute-text")), + Map.entry("0x0463", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-actual-volume", + "pid-info-actual-volume-text")), + Map.entry("0x0464", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-allowed-playback-actions", "pid-allowed-playback-actions-text")), + Map.entry("0x0465", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-group-membership", "pid-info-group-membership-text")), + Map.entry("0x0466", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-playing-favorite", "pid-info-playing-favorite-text")), + Map.entry("0x0467", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-absolute-group-volume-control", "pid-absolute-group-volume-control-text")), + Map.entry("0x0468", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-absolute-group-volume", "pid-info-absolute-group-volume-text")), + Map.entry("0x0469", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-media-source", + "pid-media-source-text")), + Map.entry("0x04A0", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-solar-power-production", "pid-solar-power-production-text")), + Map.entry("0x04A1", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-inverter-output-power", "pid-inverter-output-power-text")), + Map.entry("0x04A2", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-solar-energy-(today)", + "pid-solar-energy-(today)-text")), + Map.entry("0x04A3", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-injected-energy-(today)", "pid-injected-energy-(today)-text")), + Map.entry("0x04A4", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-purchased-energy-(today)", "pid-purchased-energy-(today)-text")), + Map.entry("0x04A5", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-inverter-alarm", + "pid-inverter-alarm-text")), + Map.entry("0x04A6", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-self-consumption", + "pid-self-consumption-text")), + Map.entry("0x04A7", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-self-sufficiency", + "pid-self-sufficiency-text")), + Map.entry("0x04A8", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-home-power-consumption", "pid-home-power-consumption-text")), + Map.entry("0x04A9", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-power-to-grid", + "pid-power-to-grid-text")), + Map.entry("0x04AA", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-consumed-energy-(today)", "pid-consumed-energy-(today)-text")), + Map.entry("0x04AB", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-meter-alarm", + "pid-meter-alarm-text")), + Map.entry("0x04AC", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-battery-level", + "pid-battery-level-text")), + Map.entry("0x04AD", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-battery-power", + "pid-battery-power-text")), + Map.entry("0x04B0", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-boost", + "pid-boost-text")), + Map.entry("0x04B1", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-stop-charging-reuqest", "pid-stop-charging-reuqest-text")), + Map.entry("0x04B2", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-enable-charging-reuqest", "pid-enable-charging-reuqest-text")), + Map.entry("0x04B3", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-boost", + "pid-info-boost-text")), + Map.entry("0x04B4", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-wallbox-status", + "pid-info-wallbox-status-text")), + Map.entry("0x04B5", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-charging", + "pid-info-charging-text")), + Map.entry("0x04B6", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-charging-enabled", "pid-info-charging-enabled-text")), + Map.entry("0x04B7", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-installed-power", + "pid-info-installed-power-text")), + Map.entry("0x04B8", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-transmitted-energy", "pid-info-transmitted-energy-text")), + Map.entry("0x04B9", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-car-range", + "pid-info-car-range-text")), + Map.entry("0x04BA", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-charging-duration", "pid-info-charging-duration-text")), + Map.entry("0x04BB", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-current-limit", + "pid-info-current-limit-text")), + Map.entry("0x04BC", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-current-limit-for-group", "pid-info-current-limit-for-group-text")), + Map.entry("0x04BD", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-album-cover-url", + "pid-album-cover-url-text")), + Map.entry("0x0501", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-secure@home-central-unit", "pid-secure@home-central-unit-text")), + Map.entry("0x0502", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-domusdisarmcounter", + "pid-domusdisarmcounter-text")), + Map.entry("0x0504", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-intrusion-alarm", + "pid-intrusion-alarm-text")), + Map.entry("0x0505", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-safety-alarm", + "pid-safety-alarm-text")), + Map.entry("0x0507", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-infoconfigurationstatus", "pid-infoconfigurationstatus-text")), + Map.entry("0x0508", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-enable-configuration", + "pid-enable-configuration-text")), + Map.entry("0x0509", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-disarming-led", + "pid-disarming-led-text")), + Map.entry("0x050A", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-aes-key", + "pid-aes-key-text")), + Map.entry("0x050B", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-zone-status", + "pid-zone-status-text")), + Map.entry("0x050E", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-time", + "pid-time-text")), + Map.entry("0x0600", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-start-stop", + "pid-start-stop-text")), + Map.entry("0x0601", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-pause-resume", + "pid-pause-resume-text")), + Map.entry("0x0602", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-select-program", + "pid-select-program-text")), + Map.entry("0x0603", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-delayed-start-time", + "pid-delayed-start-time-text")), + Map.entry("0x0604", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-status", + "pid-info-status-text")), + Map.entry("0x0605", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-remote-start-enabled", "pid-info-remote-start-enabled-text")), + Map.entry("0x0606", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-program", + "pid-info-program-text")), + Map.entry("0x0607", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-finish-time", + "pid-info-finish-time-text")), + Map.entry("0x0608", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-delayed-start", + "pid-info-delayed-start-text")), + Map.entry("0x0609", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-door", + "pid-info-door-text")), + Map.entry("0x060A", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-door-alarm", + "pid-info-door-alarm-text")), + Map.entry("0x060B", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-switch-supercool", + "pid-switch-supercool-text")), + Map.entry("0x060C", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-switch-superfreeze", + "pid-switch-superfreeze-text")), + Map.entry("0x060D", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-switch-supercool", "pid-info-switch-supercool-text")), + Map.entry("0x060E", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-info-switch-superfreeze", "pid-info-switch-superfreeze-text")), + Map.entry("0x060F", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-measured-temperature", + "pid-measured-temperature-text")), + Map.entry("0x0610", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-measured-temperature", + "pid-measured-temperature-text")), + Map.entry("0x0611", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-set-value-temperature", "pid-set-value-temperature-text")), + Map.entry("0x0612", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-set-value-temperature", "pid-set-value-temperature-text")), + Map.entry("0x0613", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-change-operation", + "pid-change-operation-text")), + Map.entry("0x0614", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-detailed-status-info", + "pid-detailed-status-info-text")), + Map.entry("0x0615", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-remaining-time", + "pid-info-remaining-time-text")), + Map.entry("0x0616", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-time-of-last-status-change", "pid-time-of-last-status-change-text")), + Map.entry("0x0618", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-lock-unlock-door-command", "pid-lock-unlock-door-command-text")), + Map.entry("0x0619", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-locked-unlocked", + "pid-info-locked-unlocked-text")), + Map.entry("0xF001", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-time", + "pid-time-text")), + Map.entry("0xF002", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-date", + "pid-date-text")), + Map.entry("0xF003", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-notification", + "pid-notification-text")), + Map.entry("0xF101", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", "pid-switch-entity-on-off", + "pid-switch-entity-on-off-text")), + Map.entry("0xF102", + new PIdContainerClass(PID_VALUETYPE_BOOLEAN, CATEGORY_SWITCH, "", "", + "pid-info-switch-entity-on-off", "pid-info-switch-entity-on-off-text")), + Map.entry("0xF104", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-consistency-tag", + "pid-consistency-tag-text")), + Map.entry("0xF105", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-battery-status", + "pid-battery-status-text")), + Map.entry("0xF106", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-stay-awake", + "pid-stay-awake-text")), + Map.entry("0xF107", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-proxy-switch", + "pid-proxy-switch-text")), + Map.entry("0xF108", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-proxy1", + "pid-proxy1-text")), + Map.entry("0xF109", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-proxy2", + "pid-proxy2-text")), + Map.entry("0xF10A", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-proxy4", + "pid-proxy4-text")), + Map.entry("0xF10B", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-cyclic-sleep-time", + "pid-cyclic-sleep-time-text")), + Map.entry("0xF10C", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-presence", + "pid-presence-text")), + Map.entry("0xF10D", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-measured-temperature-1", "pid-measured-temperature-1-text")), + Map.entry("0xF10E", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-standby-statistics", + "pid-standby-statistics-text")), + Map.entry("0xF10F", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-heartbeat-delay", + "pid-heartbeat-delay-text")), + Map.entry("0xF110", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", "pid-info-heartbeat-delay", + "pid-info-heartbeat-delay-text")), + Map.entry("0xFF01", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-measured-temperature-1", "pid-measured-temperature-1-text")), + Map.entry("0xFF02", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-measured-temperature-2", "pid-measured-temperature-2-text")), + Map.entry("0xFF03", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-measured-temperature-3", "pid-measured-temperature-3-text")), + Map.entry("0xFF04", + new PIdContainerClass(PID_VALUETYPE_UNKNOWN, CATEGORY_UNDEFINED, "", "", + "pid-measured-temperature-4", "pid-measured-temperature-4-text")), + Map.entry("0x061A", + new PIdContainerClass(PID_VALUETYPE_DECIMAL, CATEGORY_PRESSURE, "", "", + "pid-air-quality-pressure-value", "pid-air-quality-pressure-value-text")), + Map.entry("0x061B", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-air-quality-co2-value", "pid-air-quality-co2-value-text")), + Map.entry("0x061C", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-air-quality-co-value", + "pid-air-quality-co-value-text")), + Map.entry("0x061D", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-air-quality-no2-value", "pid-air-quality-no2-value-text")), + Map.entry("0x061E", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", "pid-air-quality-o3-value", + "pid-air-quality-o3-value-text")), + Map.entry("0x061F", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-air-quality-pm10-value", "pid-air-quality-pm10-value-text")), + Map.entry("0x0620", + new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-air-quality-pm25-value", "pid-air-quality-pm25-value-text")), + Map.entry("0x0621", new PIdContainerClass(PID_VALUETYPE_INTEGER, CATEGORY_UNDEFINED, "", "", + "pid-air-quality-voc-value", "pid-air-quality-voc-value-text"))); + + public static String getShortTextForPairingId(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.label; + } + + public static String getDescriptionTextForPairingId(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.description; + } + + public static String getValueTypeForPairingId(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.valueType; + } + + public static String getItemTypeForPairingId(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.category; + } + + public static String getCategoryForPairingId(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.category; + } + + public static String getPatternForPairingId(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.category; + } + + public static int getMax(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.max; + } + + public static int getMin(String Key) throws FreeAtHomeGeneralException { + PIdContainerClass desc = MAP_PAIRING_ID.get(Key); + + if (desc == null) { + throw new FreeAtHomeGeneralException(0, + String.format("%s - Key:%s", "PID is not in the translation table", Key)); + } + + return desc.min; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/UidUtils.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/UidUtils.java new file mode 100644 index 0000000000000..9b7f4cec65859 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/util/UidUtils.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.util; + +import static org.openhab.binding.freeathomesystem.internal.FreeAtHomeSystemBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class UidUtils { + + public static ChannelTypeUID generateChannelTypeUID(String valueType, boolean isReadOnly) { + String channelNameString; + + if (isReadOnly) { + channelNameString = valueType + "-ro"; + } else { + channelNameString = valueType; + } + + return new ChannelTypeUID(BINDING_ID, channelNameString); + } + + public static ThingTypeUID generateThingUID() { + return new ThingTypeUID(BINDING_ID, DEVICE_TYPE_ID); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BinaryValueStateConverter.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BinaryValueStateConverter.java new file mode 100644 index 0000000000000..7fc2d6339ad51 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BinaryValueStateConverter.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.valuestateconverter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.types.State; + +/** + * The {@link BinaryValueStateConverter} is a value converter for integer values with a specific mask + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class BinaryValueStateConverter implements ValueStateConverter { + + private int maskValue; + + public BinaryValueStateConverter(int mask) { + maskValue = mask; + } + + @Override + public State convertToState(String value) { + int intValue = Integer.decode(value); + int result; + + result = intValue & maskValue; + + if (maskValue == result) { + return OnOffType.ON; + } else { + return OnOffType.OFF; + } + } + + @Override + public String convertToValueString(State state) { + if (state.equals(OnOffType.ON)) { + return "1"; + } + + if (state.equals(OnOffType.OFF)) { + return "0"; + } + + return ""; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BooleanValueStateConverter.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BooleanValueStateConverter.java new file mode 100644 index 0000000000000..e07a519ee6cea --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/BooleanValueStateConverter.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.valuestateconverter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.types.State; + +/** + * The {@link DecimalValueStateConverter} is a value converter for boolean values + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class BooleanValueStateConverter implements ValueStateConverter { + + @Override + public State convertToState(String value) { + return OnOffType.from(value); + } + + @Override + public String convertToValueString(State state) { + if (state.equals(OnOffType.ON)) { + return "1"; + } + + if (state.equals(OnOffType.OFF)) { + return "0"; + } + + return ""; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/DecimalValueStateConverter.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/DecimalValueStateConverter.java new file mode 100644 index 0000000000000..5945c1871e54e --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/DecimalValueStateConverter.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.valuestateconverter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.types.State; + +/** + * The {@link DecimalValueStateConverter} is a value converter for decimal values + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class DecimalValueStateConverter implements ValueStateConverter { + + @Override + public State convertToState(String value) { + return new DecimalType(value); + } + + @Override + public String convertToValueString(State state) { + return ((DecimalType) state).toString(); + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ShuttercontrolValueStateConverter.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ShuttercontrolValueStateConverter.java new file mode 100644 index 0000000000000..66996dc50e159 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ShuttercontrolValueStateConverter.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.valuestateconverter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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.types.Command; +import org.openhab.core.types.State; + +/** + * The {@link ShuttercontrolValueStateConverter} is a value converter for shutter movement + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public class ShuttercontrolValueStateConverter implements ValueStateConverter { + + @Override + public State convertToState(String value) { + State ret = UpDownType.DOWN; + + switch (value) { + default: + case "0": + ret = new StringType("STOP"); + break; + case "2": + ret = UpDownType.UP; + break; + case "3": + ret = UpDownType.DOWN; + break; + } + + return ret; + } + + @Override + public String convertToValueString(State state) { + String valueString = "0"; + String stateString = "STOP"; + + if (state instanceof UpDownType) { + stateString = ((UpDownType) state).toString(); + } + + if (((Command) state) instanceof StopMoveType) { + stateString = "STOP"; + } + + switch (stateString) { + default: + case "STOP": + valueString = "0"; + break; + case "UP": + valueString = "0"; + break; + case "DOWN": + valueString = "1"; + break; + } + + return valueString; + } +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ValueStateConverter.java b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ValueStateConverter.java new file mode 100644 index 0000000000000..80187ff7b2f2e --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/java/org/openhab/binding/freeathomesystem/internal/valuestateconverter/ValueStateConverter.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2024 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.freeathomesystem.internal.valuestateconverter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.types.State; + +/** + * The {@link ValueStateConverter} base class for the value state converters + * + * @author Andras Uhrin - Initial contribution + * + */ +@NonNullByDefault +public interface ValueStateConverter { + public State convertToState(String value); + + public String convertToValueString(State state); +} diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 0000000000000..ccfa0e3e1e045 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,10 @@ + + + + binding + FreeAtHomeSystem Binding + This is the binding for free@home system. + local + diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/i18n/freeathomesystem.properties b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/i18n/freeathomesystem.properties new file mode 100644 index 0000000000000..9b021ffaf4635 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/i18n/freeathomesystem.properties @@ -0,0 +1,652 @@ +# add-on + +addon.freeathomesystem.name = FreeAtHomeSystem Binding +addon.freeathomesystem.description = This is the binding for free@home system. + +# thing types + +thing-type.freeathomesystem.device.label = free@home Device +thing-type.freeathomesystem.device.description = This represents the generic free@home device +thing-type.freeathomesystem.gateway.label = free@home Gateway +thing-type.freeathomesystem.gateway.description = This gateway represents the free@home Gateway + +# thing types config + +thing-type.config.freeathomesystem.device.deviceId.label = Device Id +thing-type.config.freeathomesystem.device.deviceId.description = This is the unique id of the free@home device (Please do not modify after the Thing is generated) +thing-type.config.freeathomesystem.gateway.group.identification.label = SysAP Setting +thing-type.config.freeathomesystem.gateway.group.identification.description = SysAP network address and user settings +thing-type.config.freeathomesystem.gateway.ipAddress.label = Sysap IP Address +thing-type.config.freeathomesystem.gateway.ipAddress.description = IP Address of the Busch-Jaeger Gateway +thing-type.config.freeathomesystem.gateway.password.label = Password +thing-type.config.freeathomesystem.gateway.password.description = Password for gateway +thing-type.config.freeathomesystem.gateway.username.label = User Name +thing-type.config.freeathomesystem.gateway.username.description = The login name + +# localized error text + +comm-error.not-able-open-httpconnection = Cannot open http connection, wrong password +comm-error.http-wrongpass-or-ip = Cannot open http connection, wrong password or IP address +comm-error.not-able-open-websocketconnection = Cannot open websocket connection +comm-error.general-websocket-issue = General issue in websocket connection +comm-error.websocket-keep-alive-error = Websocket keep alive error +comm-error.wrong-credentials = Wrong credentials for SysAP +comm-error.error-in-sysap-com = Error in SysAp communication +conf-error.general-binding-error = General binding error! +conf-error.datapointgroup-invalid = Datapoint group is not valid! +conf-error.inputdatapoint-invalid = Input Datapoint is not valid! +conf-error.invalid-deviceconfig = Invalid device configuration! +conf-error.invalid-bridge = Invalid bridge! +conf-error.bridge-not-configured = Bridge not configured! +conf-error.ip-address-missing = IP address is not specified! +conf-error.username-missing = User name is not specified! +conf-error.password-missing = Password is not specified! +conf-error.device-desc-null = Device description is NULL! +gen-error.no-bridge-avail = No bridge available! + +# localized info text for function id + +fid-dimming-sensor = Dimming sensor +fid-blind-sensor = Blind sensor +fid-stairwell-light-sensor = Stairwell light sensor +fid-force-on/off-sensor = Force On/Off sensor +fid-scene-sensor = Scene sensor +fid-switch-actuator = Switch actuator +fid-blind-actuator = Blind actuator +fid-room-temperature-controller-with-fan-speed-level = Room temperature controller with fan speed level +fid-room-temperature-controller-extension-unit = Room temperature controller extension unit +fid-wind-alarm = Wind Alarm +fid-frost-alarm = Frost Alarm +fid-rain-alarm = Rain Alarm +fid-window-sensor = Window sensor +fid-movement-detector = Movement Detector +fid-dim-actuator = Dim actuator +fid-radiator = Radiator +fid-underfloor-heating = Underfloor heating +fid-fan-coil = Fan Coil +fid-two-level-controller = Two-level controller +fid-door-opener = Door opener +fid-proxy = Proxy +fid-door-map.entry-system-call-level-actuator = Door Map.entry System Call Level Actuator +fid-door-map.entry-system-call-level-sensor = Door Map.entry System Call Level Sensor +fid-door-call = Door call +fid-automatic-door-opener = Automatic door opener +fid-corridor-light = Corridor light +fid-room-temperature-controller = Room temperature controller +fid-cooling-mode = Cooling mode +fid-heating-mode = Heating mode +fid-force-position-blind = Force-position blind +fid-auto.-heating/cooling-mode = Auto. heating/cooling mode +fid-switchover-heating/cooling = Switchover heating/cooling +fid-device-settings = Device settings +fid-dim-actuator = Dim actuator +fid-dim-actuator = Dim actuator +fid-control-element = Control element +fid-dimming-sensor = Dimming sensor +fid-blind-sensor = Blind sensor +fid-stairwell-light-sensor = Stairwell light sensor +fid-force-on/off-sensor = Force On/Off sensor +fid-force-position-blind = Force-position blind +fid-scene-sensor = Scene sensor +fid-room-temperature-controller-extension-unit = Room temperature controller extension unit +fid-fan-coil-sensor = Fan coil sensor +fid-rgb-+-warm-white/cold-white-sensor = RGB + warm white/cold white sensor +fid-rgb-sensor = RGB sensor +fid-warm-white/cold-white-sensor = Warm white/cold white sensor +fid-add.-stage-for-heating-mode = Add. stage for heating mode +fid-radiator-thermostate = Radiator thermostate +fid-room-temperature-controller-extension-unit = Room temperature controller extension unit +fid-brightness-sensor = Brightness sensor +fid-rain-sensor = Rain sensor +fid-temperature-sensor = Temperature sensor +fid-wind-sensor = Wind sensor +fid-trigger = Trigger +fid-heating-mode = Heating mode +fid-cooling-mode = Cooling mode +fid-auto.-heating/cooling-mode = Auto. heating/cooling mode +fid-two-valves-for-heating-and-cooling = Two valves for heating and cooling +fid-window/door = Window/Door +fid-abc = ABC +fid-abd = ABD +fid-acd = ACD +fid-timer-program-switch-sensor = Timer program switch sensor +fid-zone = Zone +fid-central-heating-actuator = Central heating actuator +fid-central-cooling-actuator = Central cooling actuator +fid-housekeeping = Housekeeping +fid-media-player = Media Player +fid-panel-room-temperature-controller-slave-for-battery-device = Panel Room Temperature Controller Slave For Battery Device +fid-media-player-sensor = Media Player Sensor +fid-roller-blind-actuator = Roller blind actuator +fid-attic-window-actuator = Attic window actuator +fid-awning-actuator = Awning actuator +fid-windowdoor-position-sensor = WindowDoor Position Sensor +fid-window/door-position = Window/Door position +fid-media-playback-control-sensor = Media playback control sensor +fid-media-volume-sensor = Media volume sensor +fid-dishwasher = Dishwasher +fid-laundry = Laundry +fid-dryer = Dryer +fid-oven = Oven +fid-fridge = Fridge +fid-freezer = Freezer +fid-hood = Hood +fid-coffee-machine = Coffee machine +fid-fridge/freezer = Fridge/Freezer +fid-timer-program-switch-sensor = Timer program switch sensor +fid-ceiling-fan-actuator = Ceiling fan actuator +fid-ceiling-fan-sensor = Ceiling fan sensor +fid-room-temperature-controller-with-fan-speed-level = Room temperature controller with fan speed level +fid-zone = Zone +fid-safety = Safety +fid-external-ir-sensor-bx80 = External IR Sensor BX80 +fid-external-ir-sensor-vxi = External IR Sensor VXI +fid-external-ir-sensor-mini = External IR Sensor Mini +fid-external-ir-sensor-high-altitude = External IR Sensor High Altitude +fid-external-ir-sensor-curtain = External IR Sensor Curtain +fid-smoke-detector = Smoke Detector +fid-carbon-monoxide-sensor = Carbon Monoxide Sensor +fid-methane-detector = Methane Detector +fid-gas-sensor-lpg = Gas Sensor LPG +fid-flood-detection = Flood Detection +fid-secure@home-central-unit = secure@home Central Unit +fid-thermostat = Thermostat +fid-secure@home-zone-sensor = secure@home Zone Sensor +fid-slave-thermostat = Slave thermostat +fid-secure@home-integration-logic = secure@home Integration Logic +fid-add.-stage-for-cooling-mode = Add. stage for cooling mode +fid-two-level-heating-actuator = Two Level Heating Actuator +fid-two-level-cooling-actuator = Two Level Cooling Actuator +fid-zone = Zone +fid-volume-up = Volume up +fid-volume-down = Volume down +fid-play/pause = Play/pause +fid-next-favorite = Next favorite +fid-next-song = Next song +fid-previous-song = Previous song +fid-home-appliance-sensor = Home appliance sensor +fid-heat-sensor = Heat sensor +fid-zone-switching = Zone switching +fid-button-function = Button function +fid-advanced-configuration = Advanced configuration +fid-secure@home-central-unit-basic = secure@home Central Unit Basic +fid-repeater = Repeater +fid-remote-scene-control = Remote scene control +fid-window-sensor = Window sensor +fid-movement-detector = Movement Detector +fid-external-ir-sensor-curtain = External IR Sensor Curtain +fid-smoke-detector = Smoke Detector +fid-flood-detection = Flood Detection +fid-sensor-for-air-conditioning-unit = Sensor for air-conditioning unit +fid-two-point-controller-for-heating-or-cooling = Two-point controller for heating or cooling +fid-slave-thermostat = Slave thermostat +fid-wallbox = Wallbox +fid-door-lock-control = Door lock control +fid-room-temperature-controller-with-fan-speed-level = Room temperature controller with fan speed level +fid-scene-trigger = Scene trigger +fid-rule-switch = Rule Switch +fid-air-quality-sensor-pressure = Air quality sensor Pressure +fid-air-quality-sensor-co2 = Air quality sensor CO2 +fid-air-quality-sensor-co = Air quality sensor CO +fid-air-quality-sensor-no2 = Air quality sensor NO2 +fid-air-quality-sensor-o3 = Air quality sensor O3 +fid-air-quality-sensor-pm10 = Air quality sensor PM10 +fid-air-quality-sensor-pm25 = Air quality sensor PM25 +fid-air-quality-sensor-voc = Air quality sensor VOC +fid-air-quality-sensor-humidity = Air quality sensor humidity +fid-movement-detector-fle = Movement detector (flex) +fid-dim-actuator-flex = Dim actuator (flex) + +# localized info text for pairing id + +pid-switch-on-off = Switch +pid-timed-start-stop = Timed Start/Stop +pid-force-position = Force-position +pid-scene-control = Scene Control +pid-movement-under-consideration-of-brightness = Movement under consideration of brightness +pid-presence = Presence +pid-relative-set-value = Relative Set Value +pid-absolute-set-value = Set Value +pid-night = Night +pid-invalid-string-id = invalid string id +pid-rgb-color = RGB color +pid-color-temperature = Color Temperature +pid-hsv = HSV +pid-hue = HUE +pid-saturation = Saturation +pid-move-up-down = Move Up/Down +pid-adjust-up-down = Adjust Up/Down +pid-set-absolute-position-blinds = Blinds Position +pid-set-absolute-position-slats = Slats Position +pid-wind-alarm = Wind Alarm +pid-frost-alarm = Frost Alarm +pid-rain-alarm = Rain Alarm +pid-force-position-blind = Force-position blind +pid-window-door-position = Window/Door position +pid-actuating-value-heating = Actuating Value Heating +pid-fan-level-heating = Fan Level Heating +pid-actuating-value-cooling = Actuating Value Cooling +pid-set-value-temperature = Set Value Temperature +pid-relative-set-point-temperature = Relative Setpoint Temperature +pid-window-door = Window/Door +pid-status-indication = Status +pid-fan-manual-heating-on-off = Fan Manual Heating +pid-controller-on-off = Controller +pid-relative-set-point-request = Relative Setpoint Request +pid-eco-mode-on-off-request = Eco mode +pid-comfort-temperature = Comfort Temperature +pid-fan-level-request = Fan Level +pid-fan-manual-on-off-request = Fan Switch +pid-controller-on-off-request = Controller Switch +pid-eco-mode-on-off-request = Eco mode +pid-info-on-off = Status info +pid-force-position-info = Force-position +pid-sysap-infoonoff = SysAP-InfoOnOff +pid-sysap-infoforce = SysAP-InfoForce +pid-info-actual-dimming-value = Dimming Value +pid-info-error = Error INfo +pid-sysap-infocurrentdimmingvalue = SysAP-InfoCurrentDimmingValue +pid-sysap-infoerror = SysAP-InfoError +pid-info-color-temperature = Color Temperature +pid-sysap-info-color-temperature = SysAP-Info Color Temperature +pid-info-hsv = HSV +pid-sysap-info-hsv = SysAP Info HSV +pid-info-color-mode = Color Mode +pid-sysap-info-color-mode = SysAP Info Color Mode +pid-info-move-up-down = Move Up/Down +pid-current-absolute-position-blinds-percentage = Blinds Position +pid-current-absolute-position-slats-percentage = Slats Position +pid-sysap-infomoveupdown = SysAP-InfoMoveUpDown +pid-sysap-infocurrentabsoluteblindspercentage = SysAP-InfoCurrentAbsoluteBlindsPercentage +pid-sysap-infocurrentabsoluteslatspercentage = SysAP-InfoCurrentAbsoluteSlatsPercentage +pid-measured-temperature = Measured Temperature +pid-info-value-heating = Heating Value +pid-info-value-cooling = cooling Value +pid-switchover-heating-cooling = Switchover heating/cooling +pid-actuating-fan-stage-heating = Fan Heating Control +pid-absolute-setpoint-temperature = Setpoint temperature +pid-additional-heating-value-info = Additional heating Info +pid-additional-cooling-value-info = Additional cooling Info +pid-control-value-additional-heating = Additional heating Control +pid-control-value-additional-cooling = Additional cooling Control +pid-info-actuating-fan-stage-heating = Fan Heating Info +pid-info-actuating-fan-manual-on-off-heating = Fan Manual On/Off Heating +pid-actuating-fan-stage-cooling = Fan Cooling Control +pid-info-fan-stage-cooling = Fan Cooling Info +pid-info-fan-manual-on-off-cooling = Fan Manual On/Off Cooling +pid-heating-active = Heating active +pid-cooling-active = Cooling active +pid-heating-demand = Heating demand +pid-cooling-demand = Cooling demand +pid-heating-demand-feedback-signal = Heating demand feedback +pid-cooling-demand-feedback-signal = Cooling demand feedback +pid-humidity = Humidity +pid-aux-on-off-request = Aux Switch +pid-aux-on-off-response = Aux Swicth response +pid-heating-on-off-request = Heating +pid-cooling-on-off-request = Cooling +pid-operation-mode = Operation mode +pid-swing-h-v = Swing H/V +pid-supported-features = Features +pid-extended-status-indication = Extended Status Indication +pid-extended-status-indication = Extended Status Indication +pid-aux-heating-on-off-request = Aux Heating +pid-emergency-heating-on-off-request = Emergency Heating +pid-relative-fan-speed-control = Relative fan speed +pid-absolute-fan-speed-control = Fan speed +pid-info-absolute-fan-speed = Fan speed feedback +pid-sysap-infoactualfanspeed = SysAP-InfoActualFanSpeed +pid-notification-flags = Notification flags +pid-power-rc = Power RC +pid-power-rh = Power RH +pid-proximity-status = Proximity status +pid-brightness-sensor = Brightness +pid-last-touch = Last touch +pid-led-backlighting-night-mode = LED backlighting +pid-locator-beep = Locator beep +pid-switch-test-alarm = Switch test alarm +pid-fire-alarm-active = Fire alarm +pid-outside-temperature = Outside temperature +pid-wind-force = Wind force +pid-brightness-alarm = Brightness alarm +pid-lux-value = Lux value +pid-wind-speed = Wind speed +pid-rain-detection = Rain detection +pid-rain-sensor-frequency = Rain sensor frequency +pid-play = Play +pid-pause = Pause +pid-next = Next +pid-previous = Previous +pid-play-mode = Play mode +pid-mute = Mute +pid-relative-volume-control = Relative volume control +pid-absolute-volume-control = Absolute volume control +pid-group-membership = Group membership +pid-play-favorite = Play favorite +pid-play-next-favorite = Play next favorite +pid-playback-status = Playback status +pid-current-item-metadata-info = Current item metadata +pid-info-mute = Mute Info +pid-info-actual-volume = Actual volume +pid-allowed-playback-actions = Playback actions +pid-info-group-membership = Group membership Info +pid-info-playing-favorite = Playing favorite Info +pid-absolute-group-volume-control = Group Volume +pid-info-absolute-group-volume = Group Volume INfo +pid-media-source = Media source +pid-solar-power-production = Power production +pid-inverter-output-power = Inverter output power +pid-solar-energy-(today) = Solar energy (today) +pid-injected-energy-(today) = Injected energy (today) +pid-purchased-energy-(today) = Purchased energy (today) +pid-inverter-alarm = Inverter alarm +pid-self-consumption = Self-consumption +pid-self-sufficiency = Self-sufficiency +pid-home-power-consumption = Home power consumption +pid-power-to-grid = Power to grid +pid-consumed-energy-(today) = Consumed energy (today) +pid-meter-alarm = Meter alarm +pid-battery-level = Battery level +pid-battery-power = Battery power +pid-boost = Boost +pid-stop-charging-reuqest = Stop charging +pid-enable-charging-reuqest = Enable charging +pid-info-boost = Boost Info +pid-info-wallbox-status = Wallbox status +pid-info-charging = Charging INfo +pid-info-charging-enabled = Charging enabled +pid-info-installed-power = Installed power +pid-info-transmitted-energy = Transmitted energy +pid-info-car-range = Car range +pid-info-charging-duration = Charging duration +pid-info-current-limit = Current limit +pid-info-current-limit-for-group = Current limit for group +pid-album-cover-url = Cover +pid-secure@home-central-unit = secure@home Central Unit +pid-domusdisarmcounter = DomusDisarmCounter +pid-intrusion-alarm = Intrusion Alarm +pid-safety-alarm = Safety Alarm +pid-infoconfigurationstatus = InfoConfigurationStatus +pid-enable-configuration = Enable configuration +pid-disarming-led = Disarming LED +pid-aes-key = AES Key +pid-zone-status = Zone status +pid-time = Time +pid-start-stop = Start / Stop +pid-pause-resume = Pause / Resume +pid-select-program = Select program +pid-delayed-start-time = Delayed start time +pid-info-status = Status +pid-info-remote-start-enabled = Remote start enabled +pid-info-program = Program +pid-info-finish-time = Finish time +pid-info-delayed-start = Delayed start +pid-info-door = Door +pid-info-door-alarm = Door alarm +pid-switch-supercool = Supercool Info +pid-switch-superfreeze = Superfreeze Info +pid-info-switch-supercool = Supercool switch +pid-info-switch-superfreeze = Superfreeze switch +pid-measured-temperature = Measured Temperature +pid-set-value-temperature = Set Value Temperature +pid-change-operation = Operation +pid-detailed-status-info = Status +pid-info-remaining-time = Remaining time +pid-time-of-last-status-change = Time of last change +pid-lock-unlock-door-command = Door lock +pid-info-locked-unlocked = Door Status +pid-time = Time +pid-date = Date +pid-notification = Notification +pid-switch-entity-on-off = Switch +pid-info-switch-entity-on-off = Switch feedback +pid-consistency-tag = Consistency Tag +pid-battery-status = Battery Status +pid-stay-awake = Stay awake! +pid-proxy-switch = Proxy switch +pid-proxy1 = Proxy 1Byte +pid-proxy2 = Proxy 2Byte +pid-proxy4 = Proxy 4Byte +pid-cyclic-sleep-time = Sleep time +pid-presence = Presence +pid-measured-temperature-1 = Measured temperature +pid-standby-statistics = Standby Statistics +pid-heartbeat-delay = Heartbeat delay +pid-info-heartbeat-delay = Heartbeat delay +pid-measured-temperature-1 = Measured temperature +pid-measured-temperature-2 = Measured temperature +pid-measured-temperature-3 = Measured temperature +pid-measured-temperature-4 = Measured temperature +pid-air-quality-pressure-value = Pressure value +pid-air-quality-co2-value = CO2 value +pid-air-quality-co-value = CO value +pid-air-quality-no2-value = NO2 value +pid-air-quality-o3-value = O3 value +pid-air-quality-pm10-value = PM10 value +pid-air-quality-pm25-value = PM25 value +pid-air-quality-voc-value = VOC value + +# localized info text for pairing id description + +pid-switch-on-off-text = Actual value of the switch +pid-timed-start-stop-text = Switch for staircase lightning or movement detection +pid-force-position-text = Forces value dependent high priority on or off state +pid-scene-control-text = Recall or learn the set value related to encoded scene number +pid-movement-under-consideration-of-brightness-text = Activation of an autonomous switch off function triggered by an movement detector +pid-presence-text = Presence triggered by a movement detector to be used by other devices +pid-relative-set-value-text = Relative dimming value +pid-absolute-set-value-text = Controls the set value +pid-night-text = Toggle between day and night (where day = 0 / night = 1) +pid-invalid-string-id-text = Resets load failures / short circuits / etc +pid-rgb-color-text = RGB Color value of the light +pid-color-temperature-text = Color temperature value of the light +pid-hue-text = Hue value of the light +pid-saturation-text = Saturation value of the light +pid-move-up-down-text = Moves sunblind up and down +pid-adjust-up-down-text = Stops the sunblind and to step it up/down +pid-set-absolute-position-blinds-text = Moves the sunblinds into a specified position +pid-set-absolute-position-slats-text = Moves the slats into a specified position +pid-rain-alarm-text = State of the rain sensor (sent cyclically and on COV) +pid-force-position-blind-text = Forces value dependent high priority up or down state +pid-window-door-position-text = Delivers position for Window/Door (Open / Tilted / Closed) +pid-actuating-value-heating-text = Determines the through flow volume of the control valve +pid-fan-level-heating-text = Display value of the fan coil speed. (0=off / 1=lowest - 5=fastest) +pid-actuating-value-cooling-text = Determines the through flow volume of the control valve +pid-set-value-temperature-text = Defines the displayed set point temperature of the system +pid-relative-set-point-temperature-text = Defines the relative set point temperature of the system +pid-window-door-text = Status indication - Open = 1 / closed = 0 +pid-status-indication-text = States indication - on/off heating/cool +pid-fan-manual-heating-on-off-text = Switches Fan in manual control mode (master to slave) +pid-controller-on-off-text = Switches controller on or off. Off means protection mode +pid-relative-set-point-request-text = Request for a new relative set point value +pid-eco-mode-on-off-request-text = Switches eco mode on or off +pid-comfort-temperature-text = Sends the current comfort temperature +pid-fan-level-request-text = Request for a new manual fan stage +pid-fan-manual-on-off-request-text = WARNING: DO NOT USE!!!! Request for switching fan in manual/auto mode +pid-controller-on-off-request-text = Request for switching controller on or off. Off means protection mode +pid-eco-mode-on-off-request-text = Indicates ECO mode +pid-info-on-off-text = Reflects the binary state of the actuator +pid-force-position-info-text = Indicates the cause of forced operation (0 = not forced) +pid-sysap-infoonoff-text = Reflects the binary state of the actuator group +pid-sysap-infoforce-text = Indicates whether the actuator group is forced (1) or not forced (0) +pid-info-actual-dimming-value-text = Reflects the actual value of the actuator +pid-info-error-text = Indicates load failures / short circuits / etc +pid-sysap-infocurrentdimmingvalue-text = Reflects the actual value of the actuator group +pid-sysap-infoerror-text = Indicates load failures / short circuits / etc +pid-info-color-temperature-text = Color temperature +pid-sysap-info-color-temperature-text = Color temperature +pid-info-hsv-text = Hue (2 Byte) Saturation (1 Byte) +pid-sysap-info-hsv-text = Hue (2 Byte) Saturation (1 Byte) +pid-info-color-mode-text = Hsv or color mode +pid-sysap-info-color-mode-text = Hsv or color mode info +pid-info-move-up-down-text = Indicates the status of moving and last moving direction +pid-current-absolute-position-blinds-percentage-text = Indicate the current position of the sunblinds in percentage +pid-current-absolute-position-slats-percentage-text = Indicate the current position of the slats in percentage +pid-sysap-infomoveupdown-text = Indicates the status of moving and last moving direction of the actuator group +pid-sysap-infocurrentabsoluteblindspercentage-text = Indicate the current position of the sunblinds in percentage of the actuator group +pid-sysap-infocurrentabsoluteslatspercentage-text = Indicate the current position of the slats in percentage of the actuator group +pid-measured-temperature-text = Indicates the actual measured temperature +pid-info-value-heating-text = States the current flow volume of the control valve +pid-info-value-cooling-text = States the current flow volume of the control valve +pid-switchover-heating-cooling-text = Switch between heating and cooling: heating = 0 / cooling = 1 +pid-actuating-fan-stage-heating-text = Requests a new manual fan stage from actuator in heating mode +pid-absolute-setpoint-temperature-text = Set point temperature input for timer +pid-additional-heating-value-info-text = Heating feedback +pid-additional-cooling-value-info-text = Cooling feedback +pid-info-actuating-fan-stage-heating-text = Feedback from actuating fan stage +pid-info-actuating-fan-manual-on-off-heating-text = Feedback from actuating fan stage (manual on/off) +pid-actuating-fan-stage-cooling-text = Requests a new manual fan stage from actuator in cooling mode +pid-info-fan-stage-cooling-text = Feedback for current fan stage in cooling mode +pid-info-fan-manual-on-off-cooling-text = Feedback for manual fan control cooling mode +pid-heating-active-text = Heating active +pid-cooling-active-text = Cooling active +pid-heating-demand-text = Heating demand +pid-cooling-demand-text = Cooling demand +pid-heating-demand-feedback-signal-text = Heating demand feedback signal +pid-cooling-demand-feedback-signal-text = Cooling demand feedback signal +pid-humidity-text = Measured Humidity +pid-aux-on-off-request-text = Aux On/Off +pid-aux-on-off-response-text = Aux On/Off response +pid-heating-on-off-request-text = Heating On/Off +pid-cooling-on-off-request-text = Cooling On/Off +pid-operation-mode-text = Operation mode +pid-swing-h-v-text = Swing H/V +pid-supported-features-text = Supported features +pid-extended-status-indication-text = Extended Status Indication +pid-aux-heating-on-off-request-text = Aux Heating On Off Reques +pid-emergency-heating-on-off-request-text = Emergency Heating On Off Request +pid-relative-fan-speed-control-text = Relative control of the set value +pid-absolute-fan-speed-control-text = Absolute control of the set value +pid-info-absolute-fan-speed-text = Reflects the actual value of the actuator +pid-sysap-infoactualfanspeed-text = Reflects the actual value of the actuator +pid-notification-flags-text = Notifications of RF devices (e. g. Battery low) +pid-power-rc-text = Bool Value 1 +pid-power-rh-text = Bool Value 2 +pid-proximity-status-text = Bool Value 3 +pid-brightness-sensor-text = Scaling Value 1 +pid-last-touch-text = Scaling Value 2 +pid-led-backlighting-night-mode-text = Scaling Value 3 +pid-locator-beep-text = Locator Beep +pid-switch-test-alarm-text = Switch Test Alarm +pid-fire-alarm-active-text = Fire-Alarm Active +pid-outside-temperature-text = Outdoor Temperature +pid-wind-force-text = Wind force +pid-brightness-alarm-text = Brightness alarm +pid-lux-value-text = Weatherstation brightness level +pid-wind-speed-text = Wind speed +pid-wind-alarm-text = Triggers if the wind speed is higher than the threshold +pid-frost-alarm-text = Triggers if the temperature is lower than the threshold +pid-rain-detection-text = Rain detection +pid-rain-sensor-frequency-text = Rain sensor frequency +pid-play-text = Start playing +pid-pause-text = Pause/Stop playing +pid-next-text = Play next title +pid-previous-text = Play previous title +pid-play-mode-text = Play mode (shuffle / repeat) +pid-relative-volume-control-text = Relative volume control. See also relative dimming +pid-absolute-volume-control-text = Set player volume +pid-group-membership-text = Group membership +pid-play-favorite-text = Play favorite +pid-play-next-favorite-text = Play next favorite +pid-playback-status-text = Playback status +pid-current-item-metadata-info-text = Current item metadata info +pid-info-mute-text = Info mute +pid-info-actual-volume-text = Info actual volume +pid-allowed-playback-actions-text = Allowed playback actions +pid-info-playing-favorite-text = Info playing favorite +pid-absolute-group-volume-control-text = Group Volume Control +pid-info-absolute-group-volume-text = Info Group Volume +pid-media-source-text = Media source +pid-solar-power-production-text = Power from the sun +pid-inverter-output-power-text = Output power of inverter (pbatt+Psun) +pid-solar-energy-(today)-text = Produced Energy +pid-injected-energy-(today)-text = Energy into the grid +pid-purchased-energy-(today)-text = Energy from the grid +pid-inverter-alarm-text = Inverter is working in stand alone mode +pid-self-consumption-text = production PV/ Total consumption +pid-self-sufficiency-text = Consumption from PV/ Total consumption +pid-home-power-consumption-text = Power in home (PV and grid) +pid-power-to-grid-text = Power from and to the grid: Purchased (less than 0), Injection (more than 0) +pid-consumed-energy-(today)-text = Energy bought from grid per day +pid-meter-alarm-text = Meter communication loss +pid-battery-level-text = Battery level +pid-battery-power-text = Batter power: Discharge (less then 0), Charge (more then 0) +pid-boost-text = Boos status - 1: Boost enable request, 0: boost disable request +pid-stop-charging-reuqest-text = Stop charging request - 1: Stop charging session requested, 0: n/a, will be resetted when cable is unplugged +pid-enable-charging-reuqest-text = Enable charging - 1: Enable charging when cable is plugged in, 0: Disable next charging session but charge until cable is plugged +pid-info-boost-text = Boots info - 1: Boost enabled, 0: boost disabled +pid-info-wallbox-status-text = Wallbox status 00000001: car plugged in, 00000002: Authorization granted, 00000004: Not charging, battery fully loaded, 40000000: charging stopped due to blackout prevention, 80000000: Ground fault error +pid-info-charging-text = Charging status - 1: Charging, 0: Not charging +pid-info-charging-enabled-text = Charging status - 1: Charging enabled for next session, 0: Charging disabled for next session +pid-info-installed-power-text = Installed power (e.g. 20 kW) +pid-info-transmitted-energy-text = Energy transmitted so far per session (in Wh) +pid-info-car-range-text = Car range in km per sessions +pid-info-charging-duration-text = Start of charging session (in minutes in UTC) +pid-info-current-limit-text = Limit for charger (in kW) +pid-info-current-limit-for-group-text = Limit for group of charger (in kW) +pid-album-cover-url-text = Album cover URL +pid-secure@home-central-unit-text = Encrypted control datapoint for domus alarm center +pid-domusdisarmcounter-text = Info about the next counter to disarm the system +pid-intrusion-alarm-text = Intrusion alarm active +pid-safety-alarm-text = Safety Alarm active +pid-infoconfigurationstatus-text = Domus alarm device negative feedback and configuration info. +pid-enable-configuration-text = Encrypted control datapoint for entering configuration mode +pid-disarming-led-text = Arm/Disarm a Zone +pid-aes-key-text = Manufacturer ID + Serial + AES Key +pid-zone-status-text = Zone status +pid-time-text = Absolute number of seconds when the zone will be armed +pid-start-stop-text = Starts / Stops operation +pid-pause-resume-text = Pause / Resume +pid-select-program-text = Select program +pid-delayed-start-time-text = Delayed start time +pid-info-status-text = Info status +pid-info-remote-start-enabled-text = Info remote start enabled +pid-info-program-text = Info program +pid-info-finish-time-text = Info finish time +pid-info-delayed-start-text = Info delayed start +pid-info-door-text = Info door +pid-info-door-alarm-text = Info door alarm +pid-switch-supercool-text = Switch supercool +pid-switch-superfreeze-text = Switch superfreeze +pid-info-switch-supercool-text = Info switch supercool +pid-info-switch-superfreeze-text = Info switch superfreeze +pid-measured-temperature-text = Measured Temperature +pid-measured-temperature-text = Measured Temperature +pid-set-value-temperature-text = Set Value Temperature +pid-set-value-temperature-text = Set Value Temperature +pid-change-operation-text = Change operation +pid-detailed-status-info-text = Detailed status info +pid-info-remaining-time-text = Remaining time till status change (start, finish, etc.) +pid-time-of-last-status-change-text = Time of last status change (start, finish, etc.) +pid-lock-unlock-door-command-text = Lock/Unlock door command (1 Bit) +pid-info-locked-unlocked-text = Info Lock/Unlock door(1 Bit) +pid-time-text = Current local time +pid-date-text = Current local date +pid-notification-text = Notification from message center +pid-switch-entity-on-off-text = Entity control e.g. activate an alert or timer program +pid-info-switch-entity-on-off-text = Reflects the active state of an entity e.g. alert or timer program +pid-consistency-tag-text = Notifications of RF devices (e. g. Battery low) +pid-battery-status-text = Notifications of RF devices (e. g. Battery low) +pid-stay-awake-text = Notifications of RF devices (e. g. Battery low) +pid-proxy-switch-text = Proxy switch +pid-proxy1-text = Proxy, 1 byte +pid-proxy2-text = Proxy, 2 byte +pid-proxy4-text = Proxy, 4 byte +pid-cyclic-sleep-time-text = Time of sleep cycles +pid-presence-text = SysAP presence +pid-measured-temperature-1-text = SysAP temperature +pid-standby-statistics-text = Statistics about standby usage for battery devices +pid-heartbeat-delay-text = Time period between two heartbeats +pid-info-heartbeat-delay-text = Time period between two heartbeats +pid-measured-temperature-1-text = For debug purposes +pid-measured-temperature-2-text = For debug purposes +pid-measured-temperature-3-text = For debug purposes +pid-measured-temperature-4-text = For debug purposes +pid-air-quality-pressure-value-text = Air quality sensor value - Pressure value +pid-air-quality-co2-value-text = Air quality sensor value - CO2 value +pid-air-quality-co-value-text = Air quality sensor value - CO value +pid-air-quality-no2-value-text = Air quality sensor value - NO2 value +pid-air-quality-o3-value-text = Air quality sensor value - O3 value +pid-air-quality-pm10-value-text = Air quality sensor value - PM10 value +pid-air-quality-pm25-value-text = Air quality sensor value - PM25 value +pid-air-quality-voc-value-text = Air quality sensor value - VOC value diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/bridge-type.xml b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/bridge-type.xml new file mode 100644 index 0000000000000..28b214efedb4e --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/bridge-type.xml @@ -0,0 +1,35 @@ + + + + + + This gateway represents the free@home Gateway + + + + + SysAP network address and user settings + false + + + + network-address + + IP Address of the Busch-Jaeger Gateway + + + + The login name + + + + password + Password for gateway + + + + + diff --git a/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/thing-type.xml b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/thing-type.xml new file mode 100644 index 0000000000000..fe71521474fa8 --- /dev/null +++ b/bundles/org.openhab.binding.freeathomesystem/src/main/resources/OH-INF/thing/thing-type.xml @@ -0,0 +1,24 @@ + + + + + + + + This represents the generic free@home device + + Busch&Jäger - ABB + Generic free@home Device + + deviceId + + + + This is the unique id of the free@home device (Please do not modify after the Thing is generated) + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index f60d655996beb..b35d65fd53e0e 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -146,6 +146,7 @@ org.openhab.binding.folderwatcher org.openhab.binding.folding org.openhab.binding.foobot + org.openhab.binding.freeathomesystem org.openhab.binding.freebox org.openhab.binding.freeboxos org.openhab.binding.freecurrency