diff --git a/bundles/org.openhab.binding.broadlinkthermostat/README.md b/bundles/org.openhab.binding.broadlinkthermostat/README.md index 92efe72d1db..20ee445fa39 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/README.md +++ b/bundles/org.openhab.binding.broadlinkthermostat/README.md @@ -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: diff --git a/bundles/org.openhab.binding.broadlinkthermostat/pom.xml b/bundles/org.openhab.binding.broadlinkthermostat/pom.xml index c06bf44cb1f..5d9b0c9b910 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/pom.xml +++ b/bundles/org.openhab.binding.broadlinkthermostat/pom.xml @@ -12,7 +12,7 @@ org.openhab.binding.broadlinkthermostat - openHAB Add-ons :: Bundles :: Broadlink Thermostat Binding + openHAB Add-ons :: Bundles :: Broadlink Binding diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/feature/feature.xml b/bundles/org.openhab.binding.broadlinkthermostat/src/main/feature/feature.xml index 90d232095cf..ed8409995d3 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/feature/feature.xml @@ -2,7 +2,7 @@ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features - + openhab-runtime-base openhab.tp-jaxb mvn:org.openhab.addons.bundles/org.openhab.binding.broadlinkthermostat/${project.version} 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/BroadlinkBindingConstants.java similarity index 78% rename from bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatBindingConstants.java rename to bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkBindingConstants.java index d207393bec3..0b52f6239da 100644 --- 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/BroadlinkBindingConstants.java @@ -16,13 +16,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; /** - * The {@link BroadlinkThermostatBindingConstants} class defines common constants, which are + * The {@link BroadlinkBindingConstants} class defines common constants, which are * used across the whole binding. * * @author Florian Mueller - Initial contribution */ @NonNullByDefault -public class BroadlinkThermostatBindingConstants { +public class BroadlinkBindingConstants { private static final String BINDING_ID = "broadlinkthermostat"; @@ -30,10 +30,14 @@ public class BroadlinkThermostatBindingConstants { 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"); + public static final ThingTypeUID RM_UNIVERSAL_REMOTE_THING_TYPE = new ThingTypeUID(BINDING_ID, "rmuniversaldevice"); - // List of all Channel ids + // 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"; 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/BroadlinkConfig.java similarity index 84% rename from bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatConfig.java rename to bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkConfig.java index cc2f326fefd..f016a6b89ee 100644 --- 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/BroadlinkConfig.java @@ -15,17 +15,17 @@ package org.openhab.binding.broadlinkthermostat.internal; import org.eclipse.jdt.annotation.NonNullByDefault; /** - * The {@link BroadlinkThermostatConfig} class holds the configuration properties of the thing. + * The {@link BroadlinkConfig} class holds the configuration properties of the thing. * * @author Florian Mueller - Initial contribution */ @NonNullByDefault -public class BroadlinkThermostatConfig { +public class BroadlinkConfig { private String host; private String macAddress; - public BroadlinkThermostatConfig() { + public BroadlinkConfig() { this.host = "0.0.0.0"; this.macAddress = "00:00:00:00"; } 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 index 00000000000..e58af69c75e --- /dev/null +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkHandlerFactory.java @@ -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 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/BroadlinkThermostatHandlerFactory.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatHandlerFactory.java deleted file mode 100644 index f0211f17d46..00000000000 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatHandlerFactory.java +++ /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 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/BroadlinkThermostatDiscoveryService.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java similarity index 82% rename from bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkThermostatDiscoveryService.java rename to bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java index f05025e20ae..b3264043b65 100644 --- 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/BroadlinkDiscoveryService.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.broadlinkthermostat.internal.discovery; -import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants.*; +import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkBindingConstants.*; import java.io.IOException; import java.net.InetAddress; @@ -26,7 +26,6 @@ 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; @@ -44,26 +43,26 @@ import org.slf4j.LoggerFactory; import com.github.mob41.blapi.BLDevice; /** - * The {@link BroadlinkThermostatDiscoveryService} is responsible for discovering Broadlinkthermostat devices through + * 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 BroadlinkThermostatDiscoveryService extends AbstractDiscoveryService { +public class BroadlinkDiscoveryService extends AbstractDiscoveryService { - private final Logger logger = LoggerFactory.getLogger(BroadlinkThermostatDiscoveryService.class); + private final Logger logger = LoggerFactory.getLogger(BroadlinkDiscoveryService.class); private final NetworkAddressService networkAddressService; private static final Set DISCOVERABLE_THING_TYPES_UIDS = Set.of(FLOUREON_THERMOSTAT_THING_TYPE, - UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE); + RM_UNIVERSAL_REMOTE_THING_TYPE); private static final int DISCOVERY_TIMEOUT_SECONDS = 30; private @Nullable ScheduledFuture backgroundDiscoveryFuture; @Activate - public BroadlinkThermostatDiscoveryService(@Reference NetworkAddressService networkAddressService) { + public BroadlinkDiscoveryService(@Reference NetworkAddressService networkAddressService) { super(DISCOVERABLE_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SECONDS); this.networkAddressService = networkAddressService; } @@ -82,12 +81,12 @@ public class BroadlinkThermostatDiscoveryService extends AbstractDiscoveryServic blDevices = BLDevice.discoverDevices(DISCOVERY_TIMEOUT_SECONDS * 1000); } } catch (IOException e) { - logger.debug("Error while trying to discover broadlinkthermostat devices: {}", e.getMessage()); + logger.debug("Error while trying to discover broadlink devices: {}", e.getMessage()); } - logger.debug("Discovery service found {} broadlinkthermostat devices.", blDevices.length); + logger.debug("Discovery service found {} broadlink devices.", blDevices.length); for (BLDevice dev : blDevices) { - logger.debug("Broadlinkthermostat device {} of type {} with Host {} and MAC {}", dev.getDeviceDescription(), + logger.debug("Broadlink device {} of type {} with Host {} and MAC {}", dev.getDeviceDescription(), Integer.toHexString(dev.getDeviceType()), dev.getHost(), dev.getMac()); ThingUID thingUID; @@ -100,24 +99,28 @@ public class BroadlinkThermostatDiscoveryService extends AbstractDiscoveryServic logger.debug("Discovered device with IP {} does not have a DNS name, using IP as thing UID.", dev.getHost()); } - - switch (dev.getDeviceDescription()) { + 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: - thingUID = new ThingUID(UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE, id); + logger.debug("Unknown device description '{}'.", deviceDescription); + continue; } Map properties = new HashMap<>(); - properties.put(BroadlinkThermostatBindingConstants.HOST, dev.getHost()); + properties.put(HOST, dev.getHost()); properties.put(Thing.PROPERTY_MAC_ADDRESS, dev.getMac().getMacString()); - properties.put(BroadlinkThermostatBindingConstants.DESCRIPTION, dev.getDeviceDescription()); + properties.put(DESCRIPTION, dev.getDeviceDescription()); - logger.debug("Property map: {}", properties); + logger.debug("Thing {} property map: {}", thingUID, properties); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) .withLabel(dev.getDeviceDescription() + " (" + id + ")") @@ -135,7 +138,7 @@ public class BroadlinkThermostatDiscoveryService extends AbstractDiscoveryServic @Override protected void startBackgroundDiscovery() { - logger.trace("Starting background scan for Broadlinkthermostat devices"); + logger.trace("Starting background scan for Broadlink devices"); ScheduledFuture currentBackgroundDiscoveryFuture = backgroundDiscoveryFuture; if (currentBackgroundDiscoveryFuture != null) { currentBackgroundDiscoveryFuture.cancel(true); @@ -145,7 +148,7 @@ public class BroadlinkThermostatDiscoveryService extends AbstractDiscoveryServic @Override protected void stopBackgroundDiscovery() { - logger.trace("Stopping background scan for Broadlinkthermostat devices"); + logger.trace("Stopping background scan for Broadlink devices"); @Nullable ScheduledFuture backgroundDiscoveryFuture = this.backgroundDiscoveryFuture; if (backgroundDiscoveryFuture != null && !backgroundDiscoveryFuture.isCancelled()) { @@ -180,8 +183,8 @@ public class BroadlinkThermostatDiscoveryService extends AbstractDiscoveryServic } private String getHostnameWithoutDomain(String hostname) { - String broadlinkthermostatRegex = "BroadLink-OEM[-A-Za-z0-9]{12}.*"; - if (hostname.matches(broadlinkthermostatRegex)) { + 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("\\.", "-"); 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/BroadlinkBaseHandler.java similarity index 60% rename from bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkThermostatHandler.java rename to bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkBaseHandler.java index 83cfdbd28dd..0de14277a80 100644 --- 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/BroadlinkBaseHandler.java @@ -13,12 +13,10 @@ 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.binding.broadlinkthermostat.internal.BroadlinkConfig; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; @@ -29,34 +27,31 @@ import org.slf4j.LoggerFactory; import com.github.mob41.blapi.BLDevice; /** - * The {@link BroadlinkThermostatHandler} is the device handler class for a broadlinkthermostat device. + * The {@link BroadlinkBaseHandler} is the device handler class for a broadlink device. * * @author Florian Mueller - Initial contribution */ @NonNullByDefault -public abstract class BroadlinkThermostatHandler extends BaseThingHandler { +public abstract class BroadlinkBaseHandler extends BaseThingHandler { - private final Logger logger = LoggerFactory.getLogger(BroadlinkThermostatHandler.class); + private final Logger logger = LoggerFactory.getLogger(BroadlinkBaseHandler.class); @Nullable BLDevice blDevice; - private @Nullable ScheduledFuture scanJob; - @Nullable - String host; - @Nullable - String macAddress; + 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 */ - BroadlinkThermostatHandler(Thing thing) { + BroadlinkBaseHandler(Thing thing) { super(thing); } void authenticate(boolean reauth) { - logger.debug("Authenticating with broadlinkthermostat device {}...", thing.getLabel()); + logger.debug("Authenticating with broadlink device {}...", thing.getLabel()); try { BLDevice blDevice = this.blDevice; if (blDevice != null && blDevice.auth(reauth)) { @@ -64,27 +59,14 @@ public abstract class BroadlinkThermostatHandler extends BaseThingHandler { } } catch (IOException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Error while authenticating broadlinkthermostat device " + thing.getLabel() + ":" + e.getMessage()); + "Error while authenticating broadlink device " + thing.getLabel() + ":" + e.getMessage()); } } @Override public void initialize() { - BroadlinkThermostatConfig config = getConfigAs(BroadlinkThermostatConfig.class); + BroadlinkConfig config = getConfigAs(BroadlinkConfig.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); - } } } diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/FloureonThermostatHandler.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/FloureonThermostatHandler.java index 37249620149..642e4a873d6 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/FloureonThermostatHandler.java +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/FloureonThermostatHandler.java @@ -12,11 +12,12 @@ */ 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 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 index 00000000000..a7e3d896304 --- /dev/null +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/RMUniversalRemoteHandler.java @@ -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)); + } + } +} diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/binding/binding.xml index 1b07d3ec7f9..6cdcf2947d3 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/binding/binding.xml @@ -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"> - Broadlinkthermostat Binding - This is the binding for Broadlinkthermostat devices. + Broadlink Binding + This is the binding for Broadlink devices. diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/config/config.xml index ae22ca06df3..007377ff9d9 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/config/config.xml @@ -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"> - + The hostname/IP address the device is bound to, e.g. 192.168.0.2 @@ -16,5 +16,4 @@ The unique MAC address of the device, e.g. 00:10:FA:6E:38:4A - diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/i18n/broadlinkthermostat.properties b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/i18n/broadlinkthermostat.properties index 8521440114e..2f1def91301 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/i18n/broadlinkthermostat.properties +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/i18n/broadlinkthermostat.properties @@ -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 diff --git a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/thing/thing-types.xml index b5ede01d550..69ce7865125 100644 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/resources/OH-INF/thing/thing-types.xml @@ -24,7 +24,7 @@ host - + @@ -45,9 +45,20 @@ host - + + + + A universal infrared remote + + + + + + host + + Switch @@ -132,5 +143,19 @@ The time and day of week Time - + + Switch + + Put device in infrared learning mode when turned on + + + String + + Saves the learned keys using the provided name. + + + String + + Send previously learned keys by name. +