-# 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
### 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`) |
| 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:
<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>
<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>
--- /dev/null
+/**
+ * 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";
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
+++ /dev/null
-/**
- * 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";
-}
+++ /dev/null
-/**
- * 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;
- }
-}
+++ /dev/null
-/**
- * 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;
- }
-}
--- /dev/null
+/**
+ * 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("\\.", "-");
+ }
+ }
+}
+++ /dev/null
-/**
- * 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("\\.", "-");
- }
- }
-}
--- /dev/null
+/**
+ * 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();
+ }
+}
+++ /dev/null
-/**
- * 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);
- }
- }
-}
*/
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;
* @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;
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}.
*
@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");
}
}
}
@Override
+ public void dispose() {
+ ScheduledFuture<?> currentScanJob = scanJob;
+ if (currentScanJob != null) {
+ currentScanJob.cancel(true);
+ }
+ }
+
protected void refreshData() {
AdvancedStatusInfo advancedStatusInfo = advancedStatusInfoExpiringCache.getValue();
--- /dev/null
+/**
+ * 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));
+ }
+ }
+}
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>
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>
<description>The unique MAC address of the device, e.g. 00:10:FA:6E:38:4A</description>
</parameter>
</config-description>
-
</config-description:config-descriptions>
# 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
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
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
<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>