From: GiviMAD Date: Sun, 16 Oct 2022 21:13:12 +0000 (+0200) Subject: [broadlinkthermostat] Aesthetic rename and add RM Mini (#13412) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=5762d239b39fd7d1c00f0b8d70641cfbba14bdf2;p=openhab-addons.git [broadlinkthermostat] Aesthetic rename and add RM Mini (#13412) * [broadlinkthermostat] Aesthetic rename and add RM Mini Signed-off-by: Miguel Álvarez --- diff --git a/bundles/org.openhab.binding.broadlinkthermostat/README.md b/bundles/org.openhab.binding.broadlinkthermostat/README.md index 92efe72d1d..20ee445fa3 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 c06bf44cb1..5d9b0c9b91 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 90d232095c..ed8409995d 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/BroadlinkBindingConstants.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkBindingConstants.java new file mode 100644 index 0000000000..0b52f6239d --- /dev/null +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkBindingConstants.java @@ -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 index 0000000000..f016a6b89e --- /dev/null +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkConfig.java @@ -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 index 0000000000..e58af69c75 --- /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/BroadlinkThermostatBindingConstants.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatBindingConstants.java deleted file mode 100644 index d207393bec..0000000000 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatBindingConstants.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 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 index cc2f326fef..0000000000 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/BroadlinkThermostatConfig.java +++ /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 index f0211f17d4..0000000000 --- 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/BroadlinkDiscoveryService.java b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java new file mode 100644 index 0000000000..b3264043b6 --- /dev/null +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkDiscoveryService.java @@ -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 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 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 empty if configured primary IP is not set or could not be parsed. + */ + private Optional 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 index f05025e20a..0000000000 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/discovery/BroadlinkThermostatDiscoveryService.java +++ /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 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 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 empty if configured primary IP is not set or could not be parsed. - */ - private Optional 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 index 0000000000..0de14277a8 --- /dev/null +++ b/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkBaseHandler.java @@ -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 index 83cfdbd28d..0000000000 --- a/bundles/org.openhab.binding.broadlinkthermostat/src/main/java/org/openhab/binding/broadlinkthermostat/internal/handler/BroadlinkThermostatHandler.java +++ /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); - } - } -} 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 3724962014..642e4a873d 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 0000000000..a7e3d89630 --- /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 1b07d3ec7f..6cdcf2947d 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 ae22ca06df..007377ff9d 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 8521440114..2f1def9130 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 b5ede01d55..69ce786512 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. +