]> git.basschouten.com Git - openhab-addons.git/commitdiff
[broadlinkthermostat] Aesthetic rename and add RM Mini (#13412)
authorGiviMAD <GiviMAD@users.noreply.github.com>
Sun, 16 Oct 2022 21:13:12 +0000 (23:13 +0200)
committerGitHub <noreply@github.com>
Sun, 16 Oct 2022 21:13:12 +0000 (23:13 +0200)
* [broadlinkthermostat] Aesthetic rename and add RM Mini

Signed-off-by: Miguel Álvarez <miguelwork92@gmail.com>
19 files changed:
bundles/org.openhab.binding.broadlinkthermostat/README.md
bundles/org.openhab.binding.broadlinkthermostat/pom.xml
bundles/org.openhab.binding.broadlinkthermostat/src/main/feature/feature.xml
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkConfig.java [new file with mode: 0644]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatBindingConstants.java [deleted file]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatConfig.java [deleted file]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatHandlerFactory.java [deleted file]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java [new file with mode: 0644]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkThermostatDiscoveryService.java [deleted file]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkBaseHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkThermostatHandler.java [deleted file]
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/FloureonThermostatHandler.java
bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/RMUniversalRemoteHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/binding/binding.xml
bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/i18n/broadlinkthermostat.properties
bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/thing/thing-types.xml

index 92efe72d1dbae184d1c7bc57484013386ca86944..20ee445fa3971dcb375916cec5687cab2854fe08 100644 (file)
@@ -1,20 +1,21 @@
-# Broadlink Thermostat Binding
+# Broadlink Binding
 
-The binding integrates devices based on Broadlinkthermostat controllers.
+The binding integrates devices based on broadlink controllers.
 As the binding uses the [broadlink-java-api](https://github.com/mob41/broadlink-java-api), theoretically all devices supported by the api can be integrated with this binding.
 
 ## Supported Things
 
-*Note:* So far only the Floureon Thermostat has been tested! The other things are "best guess" implementations.
+*Note:* So far only the Floureon Thermostat and Rm Mini 3 devices has been tested! The other things are "best guess" implementations.
 
-| Things                  | Description                                                   | Thing Type           |
-|-------------------------|---------------------------------------------------------------|----------------------|
-| Floureon Thermostat     | Broadlinkthermostat based Thermostat sold with the branding Floureon    | floureonthermostat   |
-| Hysen Thermostat        | Broadlinkthermostat based Thermostat sold with the branding Hysen       | hysenthermostat      |
+| Things                  | Description                                                         | Thing Type           |
+|-------------------------|---------------------------------------------------------------------|----------------------|
+| Floureon Thermostat     | broadlink based Thermostat sold with the branding Floureon          | floureonthermostat   |
+| Hysen Thermostat        | broadlink based Thermostat sold with the branding Hysen             | hysenthermostat      |
+| Rm Mini                 | broadlink based Universal Controller sold with the branding Rm Mini | rmuniversalremote    |
 
 ## Discovery
 
-Broadlinkthermostat devices are discovered on the network by sending a specific broadcast message.
+Broadlink devices are discovered on the network by sending a specific broadcast message.
 Authentication is automatically sent after creating the thing.
 
 ## Thing Configuration
@@ -30,7 +31,7 @@ The autodiscovery process finds both parts automatically.
 
 ### Floureon-/Hysenthermostat
 
-| Channel Type ID               | Item Type          | Description                                                                                                                                                                           |
+| Channel Type ID               | Item Type          | Description                                                          |
 |-------------------------------|--------------------|----------------------------------------------------------------------|
 | power                         | Switch             | Switch display on/off and enable/disables heating                    |
 | mode                          | String             | Current mode of the thermostat (`auto` or `manual`)                  |
@@ -43,6 +44,14 @@ The autodiscovery process finds both parts automatically.
 | remotelock                    | Switch             | Locks the device to only allow remote actions                        |
 | time                          | DateTime           | The time and day of week of the device                               |
 
+### RM Mini Universal Controller
+
+| Channel Type ID               | Item Type          | Description                                                          |
+|-------------------------------|--------------------|----------------------------------------------------------------------|
+| learningmode                 | Switch             | Put device in infrared learning mode when turned on                  |
+| savelearned                  | String             | Saves the learned keys using the provided name                       |
+| sendlearned                  | String             | Send previously learned keys by name                                 |
+
 ## Full Example
 
 demo.things:
index c06bf44cb1f1f24cee706e5b205bd2ce37492d2d..5d9b0c9b9109c034de195838554a381a06629461 100644 (file)
@@ -12,7 +12,7 @@
 
   <artifactId>org.openhab.binding.broadlinkthermostat</artifactId>
 
-  <name>openHAB Add-ons :: Bundles :: Broadlink Thermostat Binding</name>
+  <name>openHAB Add-ons :: Bundles :: Broadlink Binding</name>
 
   <dependencies>
     <dependency>
index 90d232095cf4925b5f82c4245255a9ec37948a34..ed8409995d348764ecacb9e48c1636eb5c48d140 100644 (file)
@@ -2,7 +2,7 @@
 <features name="org.openhab.binding.broadlinkthermostat-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
        <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
 
-       <feature name="openhab-binding-broadlinkthermostat" description="Broadlink Thermostat Binding" version="${project.version}">
+       <feature name="openhab-binding-broadlinkthermostat" description="Broadlink Binding" version="${project.version}">
                <feature>openhab-runtime-base</feature>
                <feature dependency="true">openhab.tp-jaxb</feature>
                <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.broadlinkthermostat/${project.version}</bundle>
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkBindingConstants.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkBindingConstants.java
new file mode 100644 (file)
index 0000000..0b52f62
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2010-2022 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.broadlinkthermostat.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link BroadlinkBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Florian Mueller - Initial contribution
+ */
+@NonNullByDefault
+public class BroadlinkBindingConstants {
+
+    private static final String BINDING_ID = "broadlinkthermostat";
+
+    // List of all Thing Type UIDs
+    public static final ThingTypeUID FLOUREON_THERMOSTAT_THING_TYPE = new ThingTypeUID(BINDING_ID,
+            "floureonthermostat");
+    public static final ThingTypeUID HYSEN_THERMOSTAT_THING_TYPE = new ThingTypeUID(BINDING_ID, "hysenthermostat");
+    public static final ThingTypeUID RM_UNIVERSAL_REMOTE_THING_TYPE = new ThingTypeUID(BINDING_ID, "rmuniversaldevice");
+
+    // List of Remote Infrared Channel ids
+    public static final String LEARNING_MODE = "learningmode";
+    public static final String SAVE_LEARNED = "savelearned";
+    public static final String SEND_LEARNED = "sendlearned";
+
+    // List of Thermostat Channel ids
+    public static final String ROOM_TEMPERATURE = "roomtemperature";
+    public static final String ROOM_TEMPERATURE_EXTERNAL_SENSOR = "roomtemperatureexternalsensor";
+    public static final String SETPOINT = "setpoint";
+    public static final String POWER = "power";
+    public static final String MODE = "mode";
+    public static final String SENSOR = "sensor";
+    public static final String TEMPERATURE_OFFSET = "temperatureoffset";
+    public static final String ACTIVE = "active";
+    public static final String REMOTE_LOCK = "remotelock";
+    public static final String TIME = "time";
+
+    // Config properties
+    public static final String HOST = "host";
+    public static final String DESCRIPTION = "description";
+
+    public static final String MODE_AUTO = "auto";
+    public static final String SENSOR_INTERNAL = "internal";
+    public static final String SENSOR_EXTERNAL = "external";
+}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkConfig.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkConfig.java
new file mode 100644 (file)
index 0000000..f016a6b
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2010-2022 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.broadlinkthermostat.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link BroadlinkConfig} class holds the configuration properties of the thing.
+ *
+ * @author Florian Mueller - Initial contribution
+ */
+
+@NonNullByDefault
+public class BroadlinkConfig {
+    private String host;
+    private String macAddress;
+
+    public BroadlinkConfig() {
+        this.host = "0.0.0.0";
+        this.macAddress = "00:00:00:00";
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getMacAddress() {
+        return macAddress;
+    }
+
+    public void setMacAddress(String macAddress) {
+        this.macAddress = macAddress;
+    }
+}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkHandlerFactory.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkHandlerFactory.java
new file mode 100644 (file)
index 0000000..e58af69
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2010-2022 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.broadlinkthermostat.internal;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.broadlinkthermostat.internal.handler.FloureonThermostatHandler;
+import org.openhab.binding.broadlinkthermostat.internal.handler.RMUniversalRemoteHandler;
+import org.openhab.core.OpenHAB;
+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.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link BroadlinkHandlerFactory} is responsible for creating things and thing handlers.
+ *
+ * @author Florian Mueller - Initial contribution
+ */
+@Component(configurationPid = "binding.broadlinkthermostat", service = ThingHandlerFactory.class)
+@NonNullByDefault
+public class BroadlinkHandlerFactory extends BaseThingHandlerFactory {
+    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(
+            BroadlinkBindingConstants.FLOUREON_THERMOSTAT_THING_TYPE,
+            BroadlinkBindingConstants.RM_UNIVERSAL_REMOTE_THING_TYPE,
+            BroadlinkBindingConstants.HYSEN_THERMOSTAT_THING_TYPE);
+    private static final String BROADLINK_FOLDER = Path.of(OpenHAB.getUserDataFolder(), "broadlink").toString();
+    public static final String INFRARED_FOLDER = Path.of(BROADLINK_FOLDER, "infrared_commands").toString();
+    static {
+        Logger logger = LoggerFactory.getLogger(BroadlinkHandlerFactory.class);
+        File directory = new File(BROADLINK_FOLDER);
+        if (!directory.exists()) {
+            if (directory.mkdir()) {
+                logger.info("broadlink dir created {}", BROADLINK_FOLDER);
+            }
+        }
+        File childDirectory = new File(INFRARED_FOLDER);
+        if (!childDirectory.exists()) {
+            if (childDirectory.mkdir()) {
+                logger.info("infrared_commands dir created {}", INFRARED_FOLDER);
+            }
+        }
+    }
+
+    @Override
+    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+        return SUPPORTED_THING_TYPES.contains(thingTypeUID);
+    }
+
+    @Override
+    protected @Nullable ThingHandler createHandler(Thing thing) {
+        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+        if (BroadlinkBindingConstants.FLOUREON_THERMOSTAT_THING_TYPE.equals(thingTypeUID)
+                || BroadlinkBindingConstants.HYSEN_THERMOSTAT_THING_TYPE.equals(thingTypeUID)) {
+            return new FloureonThermostatHandler(thing);
+        }
+        if (BroadlinkBindingConstants.RM_UNIVERSAL_REMOTE_THING_TYPE.equals(thingTypeUID)) {
+            return new RMUniversalRemoteHandler(thing);
+        }
+        return null;
+    }
+}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatBindingConstants.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatBindingConstants.java
deleted file mode 100644 (file)
index d207393..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.broadlinkthermostat.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.thing.ThingTypeUID;
-
-/**
- * The {@link BroadlinkThermostatBindingConstants} class defines common constants, which are
- * used across the whole binding.
- *
- * @author Florian Mueller - Initial contribution
- */
-@NonNullByDefault
-public class BroadlinkThermostatBindingConstants {
-
-    private static final String BINDING_ID = "broadlinkthermostat";
-
-    // List of all Thing Type UIDs
-    public static final ThingTypeUID FLOUREON_THERMOSTAT_THING_TYPE = new ThingTypeUID(BINDING_ID,
-            "floureonthermostat");
-    public static final ThingTypeUID HYSEN_THERMOSTAT_THING_TYPE = new ThingTypeUID(BINDING_ID, "hysenthermostat");
-    public static final ThingTypeUID UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE = new ThingTypeUID(BINDING_ID,
-            "unknownbroadlinkthermostatdevice");
-
-    // List of all Channel ids
-    public static final String ROOM_TEMPERATURE = "roomtemperature";
-    public static final String ROOM_TEMPERATURE_EXTERNAL_SENSOR = "roomtemperatureexternalsensor";
-    public static final String SETPOINT = "setpoint";
-    public static final String POWER = "power";
-    public static final String MODE = "mode";
-    public static final String SENSOR = "sensor";
-    public static final String TEMPERATURE_OFFSET = "temperatureoffset";
-    public static final String ACTIVE = "active";
-    public static final String REMOTE_LOCK = "remotelock";
-    public static final String TIME = "time";
-
-    // Config properties
-    public static final String HOST = "host";
-    public static final String DESCRIPTION = "description";
-
-    public static final String MODE_AUTO = "auto";
-    public static final String SENSOR_INTERNAL = "internal";
-    public static final String SENSOR_EXTERNAL = "external";
-}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatConfig.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatConfig.java
deleted file mode 100644 (file)
index cc2f326..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.broadlinkthermostat.internal;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * The {@link BroadlinkThermostatConfig} class holds the configuration properties of the thing.
- *
- * @author Florian Mueller - Initial contribution
- */
-
-@NonNullByDefault
-public class BroadlinkThermostatConfig {
-    private String host;
-    private String macAddress;
-
-    public BroadlinkThermostatConfig() {
-        this.host = "0.0.0.0";
-        this.macAddress = "00:00:00:00";
-    }
-
-    public String getHost() {
-        return host;
-    }
-
-    public void setHost(String host) {
-        this.host = host;
-    }
-
-    public String getMacAddress() {
-        return macAddress;
-    }
-
-    public void setMacAddress(String macAddress) {
-        this.macAddress = macAddress;
-    }
-}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatHandlerFactory.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatHandlerFactory.java
deleted file mode 100644 (file)
index f0211f1..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.broadlinkthermostat.internal;
-
-import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants.*;
-
-import java.util.Set;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.broadlinkthermostat.internal.handler.FloureonThermostatHandler;
-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.annotations.Component;
-
-/**
- * The {@link BroadlinkThermostatHandlerFactory} is responsible for creating things and thing handlers.
- *
- * @author Florian Mueller - Initial contribution
- */
-@Component(configurationPid = "binding.broadlinkthermostat", service = ThingHandlerFactory.class)
-@NonNullByDefault
-public class BroadlinkThermostatHandlerFactory extends BaseThingHandlerFactory {
-
-    private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(FLOUREON_THERMOSTAT_THING_TYPE,
-            HYSEN_THERMOSTAT_THING_TYPE, UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE);
-
-    @Override
-    public boolean supportsThingType(ThingTypeUID thingTypeUID) {
-        return SUPPORTED_THING_TYPES.contains(thingTypeUID);
-    }
-
-    @Override
-    protected @Nullable ThingHandler createHandler(Thing thing) {
-        ThingTypeUID thingTypeUID = thing.getThingTypeUID();
-
-        if (FLOUREON_THERMOSTAT_THING_TYPE.equals(thingTypeUID) || HYSEN_THERMOSTAT_THING_TYPE.equals(thingTypeUID)) {
-            return new FloureonThermostatHandler(thing);
-        }
-        return null;
-    }
-}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java
new file mode 100644 (file)
index 0000000..b326404
--- /dev/null
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) 2010-2022 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.broadlinkthermostat.internal.discovery;
+
+import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkBindingConstants.*;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+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.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.net.NetworkAddressService;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.mob41.blapi.BLDevice;
+
+/**
+ * The {@link BroadlinkDiscoveryService} is responsible for discovering Broadlink devices through
+ * Broadcast.
+ *
+ * @author Florian Mueller - Initial contribution
+ */
+@Component(service = DiscoveryService.class, configurationPid = "discovery.broadlinkthermostat")
+@NonNullByDefault
+public class BroadlinkDiscoveryService extends AbstractDiscoveryService {
+
+    private final Logger logger = LoggerFactory.getLogger(BroadlinkDiscoveryService.class);
+
+    private final NetworkAddressService networkAddressService;
+
+    private static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Set.of(FLOUREON_THERMOSTAT_THING_TYPE,
+            RM_UNIVERSAL_REMOTE_THING_TYPE);
+    private static final int DISCOVERY_TIMEOUT_SECONDS = 30;
+    private @Nullable ScheduledFuture<?> backgroundDiscoveryFuture;
+
+    @Activate
+    public BroadlinkDiscoveryService(@Reference NetworkAddressService networkAddressService) {
+        super(DISCOVERABLE_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SECONDS);
+        this.networkAddressService = networkAddressService;
+    }
+
+    private void createScanner() {
+
+        long timestampOfLastScan = getTimestampOfLastScan();
+        BLDevice[] blDevices = new BLDevice[0];
+        try {
+            @Nullable
+            InetAddress sourceAddress = getIpAddress();
+            if (sourceAddress != null) {
+                logger.debug("Using source address {} for sending out broadcast request.", sourceAddress);
+                blDevices = BLDevice.discoverDevices(sourceAddress, 0, DISCOVERY_TIMEOUT_SECONDS * 1000);
+            } else {
+                blDevices = BLDevice.discoverDevices(DISCOVERY_TIMEOUT_SECONDS * 1000);
+            }
+        } catch (IOException e) {
+            logger.debug("Error while trying to discover broadlink devices: {}", e.getMessage());
+        }
+        logger.debug("Discovery service found {} broadlink devices.", blDevices.length);
+
+        for (BLDevice dev : blDevices) {
+            logger.debug("Broadlink device {} of type {} with Host {} and MAC {}", dev.getDeviceDescription(),
+                    Integer.toHexString(dev.getDeviceType()), dev.getHost(), dev.getMac());
+
+            ThingUID thingUID;
+            String id = dev.getHost().replaceAll("\\.", "-");
+            logger.debug("Device ID with IP address replacement: {}", id);
+            try {
+                id = getHostnameWithoutDomain(InetAddress.getByName(dev.getHost()).getHostName());
+                logger.debug("Device ID with DNS name: {}", id);
+            } catch (UnknownHostException e) {
+                logger.debug("Discovered device with IP {} does not have a DNS name, using IP as thing UID.",
+                        dev.getHost());
+            }
+            var deviceDescription = dev.getDeviceDescription();
+            switch (deviceDescription) {
+                case "Floureon Thermostat":
+                    thingUID = new ThingUID(FLOUREON_THERMOSTAT_THING_TYPE, id);
+                    break;
+                case "Hysen Thermostat":
+                    thingUID = new ThingUID(HYSEN_THERMOSTAT_THING_TYPE, id);
+                    break;
+                case "RM Mini":
+                    thingUID = new ThingUID(RM_UNIVERSAL_REMOTE_THING_TYPE, id);
+                    break;
+                default:
+                    logger.debug("Unknown device description '{}'.", deviceDescription);
+                    continue;
+            }
+
+            Map<String, Object> properties = new HashMap<>();
+            properties.put(HOST, dev.getHost());
+            properties.put(Thing.PROPERTY_MAC_ADDRESS, dev.getMac().getMacString());
+            properties.put(DESCRIPTION, dev.getDeviceDescription());
+
+            logger.debug("Thing {} property map: {}", thingUID, properties);
+
+            DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
+                    .withLabel(dev.getDeviceDescription() + " (" + id + ")")
+                    .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
+
+            thingDiscovered(discoveryResult);
+        }
+        removeOlderResults(timestampOfLastScan);
+    }
+
+    @Override
+    protected void startScan() {
+        scheduler.execute(this::createScanner);
+    }
+
+    @Override
+    protected void startBackgroundDiscovery() {
+        logger.trace("Starting background scan for Broadlink devices");
+        ScheduledFuture<?> currentBackgroundDiscoveryFuture = backgroundDiscoveryFuture;
+        if (currentBackgroundDiscoveryFuture != null) {
+            currentBackgroundDiscoveryFuture.cancel(true);
+        }
+        backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::createScanner, 0, 60, TimeUnit.SECONDS);
+    }
+
+    @Override
+    protected void stopBackgroundDiscovery() {
+        logger.trace("Stopping background scan for Broadlink devices");
+        @Nullable
+        ScheduledFuture<?> backgroundDiscoveryFuture = this.backgroundDiscoveryFuture;
+        if (backgroundDiscoveryFuture != null && !backgroundDiscoveryFuture.isCancelled()) {
+            if (backgroundDiscoveryFuture.cancel(true)) {
+                this.backgroundDiscoveryFuture = null;
+            }
+        }
+        stopScan();
+    }
+
+    private @Nullable InetAddress getIpAddress() {
+        return getIpFromNetworkAddressService().orElse(null);
+    }
+
+    /**
+     * Uses openHAB's NetworkAddressService to determine the local primary network interface.
+     *
+     * @return local ip or <code>empty</code> if configured primary IP is not set or could not be parsed.
+     */
+    private Optional<InetAddress> getIpFromNetworkAddressService() {
+        String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
+        if (ipAddress == null) {
+            logger.warn("No network interface could be found.");
+            return Optional.empty();
+        }
+        try {
+            return Optional.of(InetAddress.getByName(ipAddress));
+        } catch (UnknownHostException e) {
+            logger.warn("Configured primary IP cannot be parsed: {} Details: {}", ipAddress, e.getMessage());
+            return Optional.empty();
+        }
+    }
+
+    private String getHostnameWithoutDomain(String hostname) {
+        String broadlinkRegex = "BroadLink-OEM[-A-Za-z0-9]{12}.*";
+        if (hostname.matches(broadlinkRegex)) {
+            String[] dotSeparatedString = hostname.split("\\.");
+            logger.debug("Found original broadlink DNS name {}, removing domain", hostname);
+            return dotSeparatedString[0].replaceAll("\\.", "-");
+        } else {
+            logger.debug("DNS name does not match original broadlink name: {}, using it without modification. ",
+                    hostname);
+            return hostname.replaceAll("\\.", "-");
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkThermostatDiscoveryService.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkThermostatDiscoveryService.java
deleted file mode 100644 (file)
index f05025e..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.broadlinkthermostat.internal.discovery;
-
-import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants.*;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-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.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants;
-import org.openhab.core.config.discovery.AbstractDiscoveryService;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryResultBuilder;
-import org.openhab.core.config.discovery.DiscoveryService;
-import org.openhab.core.net.NetworkAddressService;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingTypeUID;
-import org.openhab.core.thing.ThingUID;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.mob41.blapi.BLDevice;
-
-/**
- * The {@link BroadlinkThermostatDiscoveryService} is responsible for discovering Broadlinkthermostat devices through
- * Broadcast.
- *
- * @author Florian Mueller - Initial contribution
- */
-@Component(service = DiscoveryService.class, configurationPid = "discovery.broadlinkthermostat")
-@NonNullByDefault
-public class BroadlinkThermostatDiscoveryService extends AbstractDiscoveryService {
-
-    private final Logger logger = LoggerFactory.getLogger(BroadlinkThermostatDiscoveryService.class);
-
-    private final NetworkAddressService networkAddressService;
-
-    private static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Set.of(FLOUREON_THERMOSTAT_THING_TYPE,
-            UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE);
-    private static final int DISCOVERY_TIMEOUT_SECONDS = 30;
-    private @Nullable ScheduledFuture<?> backgroundDiscoveryFuture;
-
-    @Activate
-    public BroadlinkThermostatDiscoveryService(@Reference NetworkAddressService networkAddressService) {
-        super(DISCOVERABLE_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SECONDS);
-        this.networkAddressService = networkAddressService;
-    }
-
-    private void createScanner() {
-
-        long timestampOfLastScan = getTimestampOfLastScan();
-        BLDevice[] blDevices = new BLDevice[0];
-        try {
-            @Nullable
-            InetAddress sourceAddress = getIpAddress();
-            if (sourceAddress != null) {
-                logger.debug("Using source address {} for sending out broadcast request.", sourceAddress);
-                blDevices = BLDevice.discoverDevices(sourceAddress, 0, DISCOVERY_TIMEOUT_SECONDS * 1000);
-            } else {
-                blDevices = BLDevice.discoverDevices(DISCOVERY_TIMEOUT_SECONDS * 1000);
-            }
-        } catch (IOException e) {
-            logger.debug("Error while trying to discover broadlinkthermostat devices: {}", e.getMessage());
-        }
-        logger.debug("Discovery service found {} broadlinkthermostat devices.", blDevices.length);
-
-        for (BLDevice dev : blDevices) {
-            logger.debug("Broadlinkthermostat device {} of type {} with Host {} and MAC {}", dev.getDeviceDescription(),
-                    Integer.toHexString(dev.getDeviceType()), dev.getHost(), dev.getMac());
-
-            ThingUID thingUID;
-            String id = dev.getHost().replaceAll("\\.", "-");
-            logger.debug("Device ID with IP address replacement: {}", id);
-            try {
-                id = getHostnameWithoutDomain(InetAddress.getByName(dev.getHost()).getHostName());
-                logger.debug("Device ID with DNS name: {}", id);
-            } catch (UnknownHostException e) {
-                logger.debug("Discovered device with IP {} does not have a DNS name, using IP as thing UID.",
-                        dev.getHost());
-            }
-
-            switch (dev.getDeviceDescription()) {
-                case "Floureon Thermostat":
-                    thingUID = new ThingUID(FLOUREON_THERMOSTAT_THING_TYPE, id);
-                    break;
-                case "Hysen Thermostat":
-                    thingUID = new ThingUID(HYSEN_THERMOSTAT_THING_TYPE, id);
-                    break;
-                default:
-                    thingUID = new ThingUID(UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE, id);
-            }
-
-            Map<String, Object> properties = new HashMap<>();
-            properties.put(BroadlinkThermostatBindingConstants.HOST, dev.getHost());
-            properties.put(Thing.PROPERTY_MAC_ADDRESS, dev.getMac().getMacString());
-            properties.put(BroadlinkThermostatBindingConstants.DESCRIPTION, dev.getDeviceDescription());
-
-            logger.debug("Property map: {}", properties);
-
-            DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
-                    .withLabel(dev.getDeviceDescription() + " (" + id + ")")
-                    .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
-
-            thingDiscovered(discoveryResult);
-        }
-        removeOlderResults(timestampOfLastScan);
-    }
-
-    @Override
-    protected void startScan() {
-        scheduler.execute(this::createScanner);
-    }
-
-    @Override
-    protected void startBackgroundDiscovery() {
-        logger.trace("Starting background scan for Broadlinkthermostat devices");
-        ScheduledFuture<?> currentBackgroundDiscoveryFuture = backgroundDiscoveryFuture;
-        if (currentBackgroundDiscoveryFuture != null) {
-            currentBackgroundDiscoveryFuture.cancel(true);
-        }
-        backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::createScanner, 0, 60, TimeUnit.SECONDS);
-    }
-
-    @Override
-    protected void stopBackgroundDiscovery() {
-        logger.trace("Stopping background scan for Broadlinkthermostat devices");
-        @Nullable
-        ScheduledFuture<?> backgroundDiscoveryFuture = this.backgroundDiscoveryFuture;
-        if (backgroundDiscoveryFuture != null && !backgroundDiscoveryFuture.isCancelled()) {
-            if (backgroundDiscoveryFuture.cancel(true)) {
-                this.backgroundDiscoveryFuture = null;
-            }
-        }
-        stopScan();
-    }
-
-    private @Nullable InetAddress getIpAddress() {
-        return getIpFromNetworkAddressService().orElse(null);
-    }
-
-    /**
-     * Uses openHAB's NetworkAddressService to determine the local primary network interface.
-     *
-     * @return local ip or <code>empty</code> if configured primary IP is not set or could not be parsed.
-     */
-    private Optional<InetAddress> getIpFromNetworkAddressService() {
-        String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
-        if (ipAddress == null) {
-            logger.warn("No network interface could be found.");
-            return Optional.empty();
-        }
-        try {
-            return Optional.of(InetAddress.getByName(ipAddress));
-        } catch (UnknownHostException e) {
-            logger.warn("Configured primary IP cannot be parsed: {} Details: {}", ipAddress, e.getMessage());
-            return Optional.empty();
-        }
-    }
-
-    private String getHostnameWithoutDomain(String hostname) {
-        String broadlinkthermostatRegex = "BroadLink-OEM[-A-Za-z0-9]{12}.*";
-        if (hostname.matches(broadlinkthermostatRegex)) {
-            String[] dotSeparatedString = hostname.split("\\.");
-            logger.debug("Found original broadlink DNS name {}, removing domain", hostname);
-            return dotSeparatedString[0].replaceAll("\\.", "-");
-        } else {
-            logger.debug("DNS name does not match original broadlink name: {}, using it without modification. ",
-                    hostname);
-            return hostname.replaceAll("\\.", "-");
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkBaseHandler.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkBaseHandler.java
new file mode 100644 (file)
index 0000000..0de1427
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2010-2022 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.broadlinkthermostat.internal.handler;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.broadlinkthermostat.internal.BroadlinkConfig;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.mob41.blapi.BLDevice;
+
+/**
+ * The {@link BroadlinkBaseHandler} is the device handler class for a broadlink device.
+ *
+ * @author Florian Mueller - Initial contribution
+ */
+@NonNullByDefault
+public abstract class BroadlinkBaseHandler extends BaseThingHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(BroadlinkBaseHandler.class);
+
+    @Nullable
+    BLDevice blDevice;
+    String host = "";
+    String macAddress = "";
+
+    /**
+     * Creates a new instance of this class for the {@link Thing}.
+     *
+     * @param thing the thing that should be handled, not null
+     */
+    BroadlinkBaseHandler(Thing thing) {
+        super(thing);
+    }
+
+    void authenticate(boolean reauth) {
+        logger.debug("Authenticating with broadlink device {}...", thing.getLabel());
+        try {
+            BLDevice blDevice = this.blDevice;
+            if (blDevice != null && blDevice.auth(reauth)) {
+                updateStatus(ThingStatus.ONLINE);
+            }
+        } catch (IOException e) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                    "Error while authenticating broadlink device " + thing.getLabel() + ":" + e.getMessage());
+        }
+    }
+
+    @Override
+    public void initialize() {
+        BroadlinkConfig config = getConfigAs(BroadlinkConfig.class);
+        host = config.getHost();
+        macAddress = config.getMacAddress();
+    }
+}
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkThermostatHandler.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkThermostatHandler.java
deleted file mode 100644 (file)
index 83cfdbd..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.broadlinkthermostat.internal.handler;
-
-import java.io.IOException;
-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.broadlinkthermostat.internal.BroadlinkThermostatConfig;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.thing.binding.BaseThingHandler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.mob41.blapi.BLDevice;
-
-/**
- * The {@link BroadlinkThermostatHandler} is the device handler class for a broadlinkthermostat device.
- *
- * @author Florian Mueller - Initial contribution
- */
-@NonNullByDefault
-public abstract class BroadlinkThermostatHandler extends BaseThingHandler {
-
-    private final Logger logger = LoggerFactory.getLogger(BroadlinkThermostatHandler.class);
-
-    @Nullable
-    BLDevice blDevice;
-    private @Nullable ScheduledFuture<?> scanJob;
-    @Nullable
-    String host;
-    @Nullable
-    String macAddress;
-
-    /**
-     * Creates a new instance of this class for the {@link Thing}.
-     *
-     * @param thing the thing that should be handled, not null
-     */
-    BroadlinkThermostatHandler(Thing thing) {
-        super(thing);
-    }
-
-    void authenticate(boolean reauth) {
-        logger.debug("Authenticating with broadlinkthermostat device {}...", thing.getLabel());
-        try {
-            BLDevice blDevice = this.blDevice;
-            if (blDevice != null && blDevice.auth(reauth)) {
-                updateStatus(ThingStatus.ONLINE);
-            }
-        } catch (IOException e) {
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                    "Error while authenticating broadlinkthermostat device " + thing.getLabel() + ":" + e.getMessage());
-        }
-    }
-
-    @Override
-    public void initialize() {
-        BroadlinkThermostatConfig config = getConfigAs(BroadlinkThermostatConfig.class);
-        host = config.getHost();
-        macAddress = config.getMacAddress();
-
-        // schedule a new scan every minute
-        scanJob = scheduler.scheduleWithFixedDelay(this::refreshData, 0, 1, TimeUnit.MINUTES);
-    }
-
-    protected abstract void refreshData();
-
-    @Override
-    public void dispose() {
-        ScheduledFuture<?> currentScanJob = scanJob;
-        if (currentScanJob != null) {
-            currentScanJob.cancel(true);
-        }
-    }
-}
index 37249620149f1892e375d54590cb757674f0d955..642e4a873d6ade0901a54e5e5ac930f633c0c99b 100644 (file)
  */
 package org.openhab.binding.broadlinkthermostat.internal.handler;
 
-import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants.*;
+import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkBindingConstants.*;
 
 import java.io.IOException;
 import java.time.LocalTime;
 import java.time.ZonedDateTime;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -49,7 +50,7 @@ import com.github.mob41.blapi.pkt.cmd.hysen.SetTimeCommand;
  * @author Florian Mueller - Initial contribution
  */
 @NonNullByDefault
-public class FloureonThermostatHandler extends BroadlinkThermostatHandler {
+public class FloureonThermostatHandler extends BroadlinkBaseHandler {
 
     private final Logger logger = LoggerFactory.getLogger(FloureonThermostatHandler.class);
     private @Nullable FloureonDevice floureonDevice;
@@ -58,6 +59,8 @@ public class FloureonThermostatHandler extends BroadlinkThermostatHandler {
     private final ExpiringCache<AdvancedStatusInfo> advancedStatusInfoExpiringCache = new ExpiringCache<>(CACHE_EXPIRY,
             this::refreshAdvancedStatus);
 
+    private @Nullable ScheduledFuture<?> scanJob;
+
     /**
      * Creates a new instance of this class for the {@link FloureonThermostatHandler}.
      *
@@ -73,16 +76,20 @@ public class FloureonThermostatHandler extends BroadlinkThermostatHandler {
     @Override
     public void initialize() {
         super.initialize();
-        if (host != null && macAddress != null) {
+        // schedule a new scan every minute
+        scanJob = scheduler.scheduleWithFixedDelay(this::refreshData, 0, 1, TimeUnit.MINUTES);
+        if (!host.isBlank() && !macAddress.isBlank()) {
             try {
                 blDevice = new FloureonDevice(host, new Mac(macAddress));
                 this.floureonDevice = (FloureonDevice) blDevice;
                 updateStatus(ThingStatus.ONLINE);
             } catch (IOException e) {
                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
-                        "Could not find broadlinkthermostat device at host" + host + "with MAC+" + macAddress + ": "
+                        "Could not find broadlink device at host " + host + " with MAC " + macAddress + ": "
                                 + e.getMessage());
             }
+        } else {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing device configuration");
         }
     }
 
@@ -247,6 +254,13 @@ public class FloureonThermostatHandler extends BroadlinkThermostatHandler {
     }
 
     @Override
+    public void dispose() {
+        ScheduledFuture<?> currentScanJob = scanJob;
+        if (currentScanJob != null) {
+            currentScanJob.cancel(true);
+        }
+    }
+
     protected void refreshData() {
 
         AdvancedStatusInfo advancedStatusInfo = advancedStatusInfoExpiringCache.getValue();
diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/RMUniversalRemoteHandler.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/RMUniversalRemoteHandler.java
new file mode 100644 (file)
index 0000000..a7e3d89
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2010-2022 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.broadlinkthermostat.internal.handler;
+
+import static org.openhab.core.library.types.OnOffType.OFF;
+import static org.openhab.core.library.types.OnOffType.ON;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.broadlinkthermostat.internal.BroadlinkBindingConstants;
+import org.openhab.binding.broadlinkthermostat.internal.BroadlinkHandlerFactory;
+import org.openhab.core.library.types.OnOffType;
+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.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.mob41.blapi.RM2Device;
+import com.github.mob41.blapi.mac.Mac;
+import com.github.mob41.blapi.pkt.cmd.rm2.SendDataCmdPayload;
+
+/**
+ * The {@link RMUniversalRemoteHandler} is responsible for handling RM Mini Universal Remotes.
+ *
+ * @author Miguel Álvarez - Initial contribution
+ */
+@NonNullByDefault
+public class RMUniversalRemoteHandler extends BroadlinkBaseHandler {
+    private final Logger logger = LoggerFactory.getLogger(RMUniversalRemoteHandler.class);
+    private @Nullable RM2Device rm2Device;
+
+    /**
+     * Creates a new instance of this class for the {@link RMUniversalRemoteHandler}.
+     *
+     * @param thing the thing that should be handled, not null
+     */
+    public RMUniversalRemoteHandler(Thing thing) {
+        super(thing);
+    }
+
+    /**
+     * Initializes a new instance of a {@link RMUniversalRemoteHandler}.
+     */
+    @Override
+    public void initialize() {
+        super.initialize();
+        if (!host.isBlank() && !macAddress.isBlank()) {
+            try {
+                blDevice = new RM2Device(host, new Mac(macAddress));
+                this.rm2Device = (RM2Device) blDevice;
+                updateStatus(ThingStatus.ONLINE);
+            } catch (IOException e) {
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+                        "Could not find broadlink universal remote device at host " + host + " with MAC " + macAddress
+                                + ": " + e.getMessage());
+            }
+        } else {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing device configuration");
+        }
+    }
+
+    @Override
+    public void handleCommand(ChannelUID channelUID, Command command) {
+        logger.debug("Command: {}", command.toFullString());
+
+        if (command == RefreshType.REFRESH) {
+            logger.debug("Nothing to refresh on thing {}", thing.getUID());
+            return;
+        }
+        authenticate(false);
+        String channelId = channelUID.getIdWithoutGroup();
+        try {
+            switch (channelId) {
+                case BroadlinkBindingConstants.LEARNING_MODE:
+                    if (OnOffType.from(command.toFullString()).equals(ON)) {
+                        handleLearningCommand();
+                    }
+                    break;
+                case BroadlinkBindingConstants.SAVE_LEARNED:
+                    handleSaveLearned(command);
+                    break;
+                case BroadlinkBindingConstants.SEND_LEARNED:
+                    handleSendLearned(command);
+                    break;
+                default:
+                    logger.debug("Command {} not supported by channel {}", command.toFullString(), channelId);
+            }
+        } catch (Exception e) {
+            logger.warn("Exception while running channel {}", channelUID);
+        }
+    }
+
+    private void handleLearningCommand() throws IOException {
+        RM2Device rm2Device = this.rm2Device;
+        if (rm2Device != null) {
+            try {
+                rm2Device.enterLearning();
+                updateState(BroadlinkBindingConstants.LEARNING_MODE, ON);
+                logger.debug("Thing {} entered learning mode", thing.getUID());
+            } catch (IOException e) {
+                updateState(BroadlinkBindingConstants.LEARNING_MODE, OFF);
+                throw e;
+            }
+        } else {
+            logger.warn("Device not initialized");
+        }
+    }
+
+    private void handleSaveLearned(Command command) throws Exception {
+        RM2Device rm2Device = this.rm2Device;
+        if (rm2Device != null) {
+            byte @Nullable [] learned = rm2Device.checkData();
+            updateState(BroadlinkBindingConstants.LEARNING_MODE, OFF);
+            if (learned == null) {
+                logger.warn("Thing {}: nothing learned", thing.getUID());
+                return;
+            }
+            var packetName = command.toFullString();
+            File destinationFile = new File(BroadlinkHandlerFactory.INFRARED_FOLDER, packetName);
+            if (destinationFile.exists()) {
+                logger.info("Key '{}' is already learned, overwriting...", packetName);
+            }
+            Files.write(destinationFile.toPath(), learned, new StandardOpenOption[] { StandardOpenOption.WRITE,
+                    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING });
+        }
+    }
+
+    private void handleSendLearned(Command command) throws IOException {
+        RM2Device rm2Device = this.rm2Device;
+        if (rm2Device != null) {
+            File packetFile = new File(BroadlinkHandlerFactory.INFRARED_FOLDER, command.toFullString());
+            if (!packetFile.exists()) {
+                logger.warn("{}: Nothing learned as '{}'", thing.getUID(), command.toFullString());
+                return;
+            }
+            var packet = Files.readAllBytes(packetFile.toPath());
+            rm2Device.sendCmdPkt(new SendDataCmdPayload(packet));
+        }
+    }
+}
index 1b07d3ec7f9701a298b9485fb7cb985a1958ad1c..6cdcf2947d3d1da6b214c87c1785393f8a626d75 100644 (file)
@@ -3,6 +3,6 @@
        xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
        xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
 
-       <name>Broadlinkthermostat Binding</name>
-       <description>This is the binding for Broadlinkthermostat devices.</description>
+       <name>Broadlink Binding</name>
+       <description>This is the binding for Broadlink devices.</description>
 </binding:binding>
index ae22ca06df329e0e0256d30fc2c1ddff102c5189..007377ff9d9f43a233b10fbd2f1885d9c3364c2d 100644 (file)
@@ -5,7 +5,7 @@
        xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
        https://openhab.org/schemas/config-description-1.0.0.xsd">
 
-       <config-description uri="thing-type:broadlinkthermostat:floureonandhysenthermostat">
+       <config-description uri="thing-type:broadlinkthermostat:deviceconfig">
                <parameter name="host" type="text" required="true">
                        <label>Hostname</label>
                        <description>The hostname/IP address the device is bound to, e.g. 192.168.0.2</description>
@@ -16,5 +16,4 @@
                        <description>The unique MAC address of the device, e.g. 00:10:FA:6E:38:4A</description>
                </parameter>
        </config-description>
-
 </config-description:config-descriptions>
index 8521440114e066dc98c0af1903e026ff8c175125..2f1def91301bfabbcd33c0040b920e4852846d37 100644 (file)
@@ -1,7 +1,7 @@
 # binding
 
-binding.broadlinkthermostat.name = Broadlinkthermostat Binding
-binding.broadlinkthermostat.description = This is the binding for Broadlinkthermostat devices.
+binding.broadlinkthermostat.name = Broadlink Binding
+binding.broadlinkthermostat.description = This is the binding for Broadlink devices.
 
 # thing types
 
@@ -9,18 +9,22 @@ thing-type.broadlinkthermostat.floureonthermostat.label = Floureon Thermostat
 thing-type.broadlinkthermostat.floureonthermostat.description = A heating device thermostat
 thing-type.broadlinkthermostat.hysenthermostat.label = Hysen Thermostat
 thing-type.broadlinkthermostat.hysenthermostat.description = A heating device thermostat
+thing-type.broadlinkthermostat.rmuniversaldevice.label = Rm Universal Device
+thing-type.broadlinkthermostat.rmuniversaldevice.description = A universal infrared remote
 
 # thing types config
 
-thing-type.config.broadlinkthermostat.floureonandhysenthermostat.host.label = Hostname
-thing-type.config.broadlinkthermostat.floureonandhysenthermostat.host.description = The hostname/IP address the device is bound to, e.g. 192.168.0.2
-thing-type.config.broadlinkthermostat.floureonandhysenthermostat.macAddress.label = MAC Address
-thing-type.config.broadlinkthermostat.floureonandhysenthermostat.macAddress.description = The unique MAC address of the device, e.g. 00:10:FA:6E:38:4A
+thing-type.config.broadlink.deviceconfig.host.label = Hostname
+thing-type.config.broadlink.deviceconfig.host.description = The hostname/IP address the device is bound to, e.g. 192.168.0.2
+thing-type.config.broadlink.deviceconfig.macAddress.label = MAC Address
+thing-type.config.broadlink.deviceconfig.macAddress.description = The unique MAC address of the device, e.g. 00:10:FA:6E:38:4A
 
 # channel types
 
 channel-type.broadlinkthermostat.active.label = Active
 channel-type.broadlinkthermostat.active.description = Shows if thermostat is currently actively heating
+channel-type.broadlinkthermostat.learningmode.label = Learning Mode
+channel-type.broadlinkthermostat.learningmode.description = Put device in infrared learning mode when turned on
 channel-type.broadlinkthermostat.mode.label = Mode
 channel-type.broadlinkthermostat.mode.description = Current mode of the thermostat
 channel-type.broadlinkthermostat.mode.state.option.auto = auto
@@ -33,6 +37,10 @@ channel-type.broadlinkthermostat.roomtemperature.label = Temperature
 channel-type.broadlinkthermostat.roomtemperature.description = Room temperature, measured directly at the device
 channel-type.broadlinkthermostat.roomtemperatureexternalsensor.label = Temperature Ext. Sensor
 channel-type.broadlinkthermostat.roomtemperatureexternalsensor.description = Room temperature, measured by the external sensor
+channel-type.broadlinkthermostat.savelearned.label = Save Learned
+channel-type.broadlinkthermostat.savelearned.description = Saves the learned keys using the provided name.
+channel-type.broadlinkthermostat.sendlearned.label = Send Learned
+channel-type.broadlinkthermostat.sendlearned.description = Send previously learned keys by name.
 channel-type.broadlinkthermostat.sensor.label = Sensor
 channel-type.broadlinkthermostat.sensor.description = The sensor (internal/external) used for triggering the thermostat
 channel-type.broadlinkthermostat.sensor.state.option.internal = internal
index b5ede01d5508e0f3aa1eb3ea5e440d34bddc4cd0..69ce78651254885c1b3ff44d4354d5175ee64c1c 100644 (file)
@@ -24,7 +24,7 @@
 
                <representation-property>host</representation-property>
 
-               <config-description-ref uri="thing-type:broadlinkthermostat:floureonandhysenthermostat"/>
+               <config-description-ref uri="thing-type:broadlinkthermostat:deviceconfig"/>
        </thing-type>
        <thing-type id="hysenthermostat">
                <label>Hysen Thermostat</label>
 
                <representation-property>host</representation-property>
 
-               <config-description-ref uri="thing-type:broadlinkthermostat:floureonandhysenthermostat"/>
+               <config-description-ref uri="thing-type:broadlinkthermostat:deviceconfig"/>
        </thing-type>
+       <thing-type id="rmuniversaldevice">
+               <label>Rm Universal Device</label>
+               <description>A universal infrared remote</description>
+               <channels>
+                       <channel id="learningmode" typeId="learningmode"/>
+                       <channel id="savelearned" typeId="savelearned"/>
+                       <channel id="sendlearned" typeId="sendlearned"/>
+               </channels>
+               <representation-property>host</representation-property>
 
+               <config-description-ref uri="thing-type:broadlinkthermostat:deviceconfig"/>
+       </thing-type>
        <channel-type id="power">
                <item-type>Switch</item-type>
                <label>Power</label>
                <description>The time and day of week</description>
                <category>Time</category>
        </channel-type>
-
+       <channel-type id="learningmode">
+               <item-type>Switch</item-type>
+               <label>Learning Mode</label>
+               <description>Put device in infrared learning mode when turned on</description>
+       </channel-type>
+       <channel-type id="savelearned">
+               <item-type>String</item-type>
+               <label>Save Learned</label>
+               <description>Saves the learned keys using the provided name.</description>
+       </channel-type>
+       <channel-type id="sendlearned">
+               <item-type>String</item-type>
+               <label>Send Learned</label>
+               <description>Send previously learned keys by name.</description>
+       </channel-type>
 </thing:thing-descriptions>