Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions bundles/org.openhab.binding.broadlinkthermostat/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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`) |
Expand All @@ -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 |
|-------------------------------|--------------------|----------------------------------------------------------------------|
| learning_mode | Switch | Put device in infrared learning mode when turned on |
| save_learned | String | Saves the learned keys using the provided name |
| send_learned | String | Send previously learned keys by name |
Comment thread
GiviMAD marked this conversation as resolved.
Outdated

## Full Example

demo.things:
Expand Down
2 changes: 1 addition & 1 deletion bundles/org.openhab.binding.broadlinkthermostat/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<artifactId>org.openhab.binding.broadlinkthermostat</artifactId>

<name>openHAB Add-ons :: Bundles :: Broadlink Thermostat Binding</name>
<name>openHAB Add-ons :: Bundles :: Broadlink Binding</name>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<features name="org.openhab.binding.broadlinkthermostat-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>

<feature name="openhab-binding-broadlinkthermostat" description="Broadlink Thermostat Binding" version="${project.version}">
<feature name="openhab-binding-broadlinkthermostat" description="Broadlink Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature dependency="true">openhab.tp-jaxb</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.broadlinkthermostat/${project.version}</bundle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,30 @@
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";

// 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");
public static final ThingTypeUID RM_UNIVERSAL_REMOTE_THING_TYPE = new ThingTypeUID(BINDING_ID, "rmuniversaldevice");
public static final ThingTypeUID UNKNOWN_broadlink_THING_TYPE = new ThingTypeUID(BINDING_ID,
Comment thread
GiviMAD marked this conversation as resolved.
Outdated
"unknownbroadlinkdevice");
Comment thread
GiviMAD marked this conversation as resolved.
Outdated

// 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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
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";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.broadlinkthermostat.internal;

import java.io.File;
import java.nio.file.Path;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.broadlinkthermostat.internal.handler.FloureonThermostatHandler;
import org.openhab.binding.broadlinkthermostat.internal.handler.RMUniversalRemoteHandler;
import org.openhab.core.OpenHAB;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link BroadlinkHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Florian Mueller - Initial contribution
*/
@Component(configurationPid = "binding.broadlinkthermostat", service = ThingHandlerFactory.class)
@NonNullByDefault
public class BroadlinkHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(
BroadlinkBindingConstants.FLOUREON_THERMOSTAT_THING_TYPE,
BroadlinkBindingConstants.RM_UNIVERSAL_REMOTE_THING_TYPE,
BroadlinkBindingConstants.HYSEN_THERMOSTAT_THING_TYPE,
BroadlinkBindingConstants.UNKNOWN_broadlink_THING_TYPE);
protected static final String BROADLINK_FOLDER = Path.of(OpenHAB.getUserDataFolder(), "broadlink").toString();
Comment thread
GiviMAD marked this conversation as resolved.
Outdated
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("porcupine dir created {}", BROADLINK_FOLDER);
Comment thread
GiviMAD marked this conversation as resolved.
Outdated
}
}
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;
Comment thread
GiviMAD marked this conversation as resolved.
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
*/
package org.openhab.binding.broadlinkthermostat.internal.discovery;

import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants.*;
Comment thread
GiviMAD marked this conversation as resolved.

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
Expand All @@ -26,7 +24,7 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants;
import org.openhab.binding.broadlinkthermostat.internal.BroadlinkBindingConstants;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
Expand All @@ -44,26 +42,27 @@
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<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Set.of(FLOUREON_THERMOSTAT_THING_TYPE,
UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE);
private static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Set.of(
BroadlinkBindingConstants.FLOUREON_THERMOSTAT_THING_TYPE,
BroadlinkBindingConstants.UNKNOWN_broadlink_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;
}
Expand All @@ -82,12 +81,12 @@ private void createScanner() {
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;
Expand All @@ -100,24 +99,26 @@ private void createScanner() {
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);
thingUID = new ThingUID(BroadlinkBindingConstants.FLOUREON_THERMOSTAT_THING_TYPE, id);
break;
case "Hysen Thermostat":
thingUID = new ThingUID(HYSEN_THERMOSTAT_THING_TYPE, id);
thingUID = new ThingUID(BroadlinkBindingConstants.HYSEN_THERMOSTAT_THING_TYPE, id);
break;
case "RM Mini":
thingUID = new ThingUID(BroadlinkBindingConstants.RM_UNIVERSAL_REMOTE_THING_TYPE, id);
break;
default:
thingUID = new ThingUID(UNKNOWN_BROADLINKTHERMOSTAT_THING_TYPE, id);
thingUID = new ThingUID(BroadlinkBindingConstants.UNKNOWN_broadlink_THING_TYPE, id);
}

Map<String, Object> properties = new HashMap<>();
properties.put(BroadlinkThermostatBindingConstants.HOST, dev.getHost());
properties.put(BroadlinkBindingConstants.HOST, dev.getHost());
properties.put(Thing.PROPERTY_MAC_ADDRESS, dev.getMac().getMacString());
properties.put(BroadlinkThermostatBindingConstants.DESCRIPTION, dev.getDeviceDescription());
properties.put(BroadlinkBindingConstants.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 + ")")
Expand All @@ -135,7 +136,7 @@ protected void startScan() {

@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);
Expand All @@ -145,7 +146,7 @@ protected void startBackgroundDiscovery() {

@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()) {
Expand Down Expand Up @@ -180,8 +181,8 @@ private Optional<InetAddress> getIpFromNetworkAddressService() {
}

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("\\.", "-");
Expand Down
Loading