added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.bluetooth.am43/.classpath
Normal file
32
bundles/org.openhab.binding.bluetooth.am43/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.bluetooth.am43/.project
Normal file
23
bundles/org.openhab.binding.bluetooth.am43/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.bluetooth.am43</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.bluetooth.am43/NOTICE
Normal file
13
bundles/org.openhab.binding.bluetooth.am43/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
73
bundles/org.openhab.binding.bluetooth.am43/README.md
Normal file
73
bundles/org.openhab.binding.bluetooth.am43/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# AM43
|
||||
|
||||
This extension adds support for [AM43 Blind Drive Motors](https://www.a-okmotors.com/am-43/).
|
||||
|
||||
## Supported Things
|
||||
|
||||
Following thing types are supported by this extension:
|
||||
|
||||
| Thing Type ID | Description |
|
||||
|---------------|-------------------------------|
|
||||
| am43 | AM43 Blind Drive Motor |
|
||||
|
||||
|
||||
## Discovery
|
||||
|
||||
As any other Bluetooth device, AM43 Blind Drive Motors are discovered automatically by the corresponding bridge.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
Supported configuration parameters `AM43 Blind Drive Motor` thing:
|
||||
|
||||
| Property | Type | Default | Required | Description |
|
||||
|---------------------------------|---------|---------|----------|--------------------------------------------------------------------------|
|
||||
| address | String | | Yes | Bluetooth address of the device (in format "XX:XX:XX:XX:XX:XX") |
|
||||
| refreshInterval | Integer | 60 | No | How often a refresh shall occur in seconds |
|
||||
| invertPosition | Boolean | false | No | Inverts the blinds percentages such that 0 becomes 100 and 100 becomes 0 |
|
||||
|
||||
## Channels
|
||||
|
||||
Following channels are supported for `AM43 Blind Drive Motor` thing:
|
||||
|
||||
| Channel ID | Item Type | Description |
|
||||
|----------------|----------------------|-------------------------------------------------------------------------------------------|
|
||||
| direction | String | The direction of the motor for UP/DOWN controls. Is either 'Forward' or 'Reverse' |
|
||||
| topLimitSet | Switch | Whether or not the top limit of the blinds has been set |
|
||||
| bottomLimitSet | Switch | Whether or not the bottom limit of the blinds has been set |
|
||||
| hasLightSensor | Switch | Whether or not the solar sensor was detected |
|
||||
| operationMode | String | Controls behavior of motor on manual button presses. Is either 'Inching' or 'Continuous' |
|
||||
| position | Rollershutter | Main rollershutter controls |
|
||||
| speed | Number:Dimensionless | The speed, in RPMs, that the motor will move the blinds |
|
||||
| length | Number:Length | The length of the blinds in millimeters. (Mostly useless) |
|
||||
| diameter | Number:Length | The diameter of the motor pulley. (Mostly useless) |
|
||||
| type | Number:Dimensionless | The type of blinds that the motor is connected to. (Mostly useless) |
|
||||
| lightLevel | Number:Dimensionless | The light level detected by the solar sensor. Will range from 0-10 |
|
||||
| electric | Number:Dimensionless | The current percent charge of the motor's battery |
|
||||
|
||||
## Example
|
||||
|
||||
am43.things (assuming you have a Bluetooth bridge with the ID `bluetooth:bluegiga:adapter1`:
|
||||
|
||||
```
|
||||
bluetooth:am43:adapter1:motor1 "AM43 Blind Drive Motor 1" (bluetooth:bluegiga:adapter1) [ address="12:34:56:78:9A:BC", refreshInterval=300, invertPosition=false ]
|
||||
```
|
||||
|
||||
am43.items:
|
||||
|
||||
```
|
||||
String direction "Direction [%s]" { channel="bluetooth:am43:adapter1:motor1:direction" }
|
||||
Switch topLimitSet "Top Limit Set" { channel="bluetooth:am43:adapter1:motor1:topLimitSet" }
|
||||
Switch bottomLimitSet "Bottom Limit Set" { channel="bluetooth:am43:adapter1:motor1:bottomLimitSet" }
|
||||
Switch hasLightSensor "Has Light Sensor" { channel="bluetooth:am43:adapter1:motor1:hasLightSensor" }
|
||||
String operationMode "Operation Mode [%s]" { channel="bluetooth:am43:adapter1:motor1:operationMode" }
|
||||
Rollershutter position "Position [%.0f %%]" { channel="bluetooth:am43:adapter1:motor1:position" }
|
||||
Number:Dimensionless speed "Speed [%.0f RPM]" { channel="bluetooth:am43:adapter1:motor1:speed" }
|
||||
Number:Length length "Length [%.0f %unit%]" { channel="bluetooth:am43:adapter1:motor1:length" }
|
||||
Number:Length diameter "Diameter [%.0f %unit%]" { channel="bluetooth:am43:adapter1:motor1:diameter" }
|
||||
Number:Dimensionless type "Type [%.0f]" { channel="bluetooth:am43:adapter1:motor1:type" }
|
||||
Number:Dimensionless light_level "Light Level [%.0f]" { channel="bluetooth:am43:adapter1:motor1:lightLevel" }
|
||||
Number:Dimensionless battery_level "Battery Level [%.0f %%]" { channel="bluetooth:am43:adapter1:motor1:electric" }
|
||||
```
|
||||
|
||||
|
||||
|
||||
25
bundles/org.openhab.binding.bluetooth.am43/pom.xml
Normal file
25
bundles/org.openhab.binding.bluetooth.am43/pom.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.bluetooth.am43</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: AM43 Bluetooth Adapter</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.bluetooth</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.bluetooth.am43-${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-bluetooth-am43" description="Bluetooth Binding AM43" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-serial</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version}</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.am43/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link AM43BindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AM43BindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "am43";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_AM43 = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID,
|
||||
BINDING_ID);
|
||||
|
||||
// List of all Channel ids
|
||||
// public static final String CHANNEL_ID_NAME = "name";
|
||||
public static final String CHANNEL_ID_DIRECTION = "direction";
|
||||
public static final String CHANNEL_ID_TOP_LIMIT_SET = "topLimitSet";
|
||||
public static final String CHANNEL_ID_BOTTOM_LIMIT_SET = "bottomLimitSet";
|
||||
public static final String CHANNEL_ID_HAS_LIGHT_SENSOR = "hasLightSensor";
|
||||
public static final String CHANNEL_ID_OPERATION_MODE = "operationMode";
|
||||
public static final String CHANNEL_ID_SPEED = "speed";
|
||||
public static final String CHANNEL_ID_ELECTRIC = "electric";
|
||||
public static final String CHANNEL_ID_POSITION = "position";
|
||||
public static final String CHANNEL_ID_LENGTH = "length";
|
||||
public static final String CHANNEL_ID_DIAMETER = "diameter";
|
||||
public static final String CHANNEL_ID_TYPE = "type";
|
||||
public static final String CHANNEL_ID_LIGHT_LEVEL = "lightLevel";
|
||||
|
||||
public static final UUID SERVICE_UUID = UUID.fromString("0000fe50-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
public static final UUID CHARACTERISTIC_UUID = UUID.fromString("0000fe51-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
public static List<String> getAllChannels() {
|
||||
return Arrays.asList(CHANNEL_ID_DIRECTION, CHANNEL_ID_TOP_LIMIT_SET, CHANNEL_ID_BOTTOM_LIMIT_SET,
|
||||
CHANNEL_ID_HAS_LIGHT_SENSOR, CHANNEL_ID_OPERATION_MODE, CHANNEL_ID_SPEED, CHANNEL_ID_ELECTRIC,
|
||||
CHANNEL_ID_POSITION, CHANNEL_ID_LENGTH, CHANNEL_ID_DIAMETER, CHANNEL_ID_TYPE, CHANNEL_ID_LIGHT_LEVEL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal;
|
||||
|
||||
/**
|
||||
* Configuration class for AM43 Binding.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
public class AM43Configuration {
|
||||
|
||||
public String address;
|
||||
public int refreshInterval;
|
||||
public boolean invertPosition;
|
||||
public int commandTimeout;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AM43Configuration [address=" + address + ", refreshInterval=" + refreshInterval + ", invertPosition="
|
||||
+ invertPosition + ", commandTimeout=" + commandTimeout + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.BluetoothBindingConstants;
|
||||
import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState;
|
||||
import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
|
||||
import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* This discovery participant is able to recognize AM43 devices and create discovery results for them.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true)
|
||||
public class AM43DiscoveryParticipant implements BluetoothDiscoveryParticipant {
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return Collections.singleton(AM43BindingConstants.THING_TYPE_AM43);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
|
||||
ThingUID thingUID = getThingUID(device);
|
||||
if (thingUID == null) {
|
||||
return null;
|
||||
}
|
||||
String label = "AM43 Blind Drive Motor";
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(BluetoothBindingConstants.CONFIGURATION_ADDRESS, device.getAddress().toString());
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, "AM43-0.45/40-ES-EB");
|
||||
properties.put(Thing.PROPERTY_VENDOR, "A-OK Precision motor Ltd.");
|
||||
Integer txPower = device.getTxPower();
|
||||
if (txPower != null) {
|
||||
properties.put(BluetoothBindingConstants.PROPERTY_TXPOWER, Integer.toString(txPower));
|
||||
}
|
||||
|
||||
// Create the discovery result and add to the inbox
|
||||
return DiscoveryResultBuilder.create(thingUID).withProperties(properties)
|
||||
.withRepresentationProperty(BluetoothBindingConstants.CONFIGURATION_ADDRESS)
|
||||
.withBridge(device.getAdapter().getUID()).withLabel(label).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
|
||||
if (device.getConnectionState() == ConnectionState.CONNECTED
|
||||
&& device.supportsService(AM43BindingConstants.SERVICE_UUID)) {
|
||||
return new ThingUID(AM43BindingConstants.THING_TYPE_AM43, device.getAdapter().getUID(),
|
||||
device.getAddress().toString().toLowerCase().replace(":", ""));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresConnection(BluetoothDiscoveryDevice device) {
|
||||
return device.getManufacturerId() == null && device.getName() != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.quantity.Length;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.BluetoothCharacteristic;
|
||||
import org.openhab.binding.bluetooth.BluetoothCompletionStatus;
|
||||
import org.openhab.binding.bluetooth.BluetoothDevice.ConnectionState;
|
||||
import org.openhab.binding.bluetooth.ConnectedBluetoothHandler;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.AM43Command;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.ControlCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.GetAllCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.GetBatteryLevelCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.GetLightLevelCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.GetPositionCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.GetSpeedCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.ResponseListener;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.SetPositionCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.SetSettingsCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.ControlAction;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.Direction;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.MotorSettings;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.OperationMode;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StopMoveType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.library.unit.MetricPrefix;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AM43Handler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AM43Handler extends ConnectedBluetoothHandler implements ResponseListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AM43Handler.class);
|
||||
|
||||
private @Nullable AM43Configuration config;
|
||||
|
||||
private volatile @Nullable AM43Command currentCommand = null;
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
|
||||
private @Nullable ExecutorService commandExecutor;
|
||||
|
||||
private @Nullable MotorSettings motorSettings = null;
|
||||
|
||||
public AM43Handler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
config = getConfigAs(AM43Configuration.class);
|
||||
|
||||
commandExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory(thing.getUID().getAsString(), true));
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
submitCommand(new GetAllCommand());
|
||||
submitCommand(new GetBatteryLevelCommand());
|
||||
submitCommand(new GetLightLevelCommand());
|
||||
}, 10, getAM43Config().refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
dispose(commandExecutor);
|
||||
dispose(currentCommand);
|
||||
dispose(refreshJob);
|
||||
|
||||
commandExecutor = null;
|
||||
currentCommand = null;
|
||||
refreshJob = null;
|
||||
motorSettings = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private static void dispose(@Nullable ExecutorService executor) {
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private static void dispose(@Nullable ScheduledFuture<?> future) {
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dispose(@Nullable AM43Command command) {
|
||||
if (command != null) {
|
||||
// even if it already completed it doesn't really matter.
|
||||
// on the off chance that the commandExecutor is waiting on the command, we can wake it up and cause it to
|
||||
// terminate
|
||||
command.setState(AM43Command.State.FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
private MotorSettings getMotorSettings() {
|
||||
MotorSettings settings = motorSettings;
|
||||
if (settings == null) {
|
||||
throw new IllegalStateException("motorSettings has not been initialized");
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private AM43Configuration getAM43Config() {
|
||||
AM43Configuration ret = config;
|
||||
if (ret == null) {
|
||||
throw new IllegalStateException("config has not been initialized");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void submitCommand(AM43Command command) {
|
||||
Executor executor = commandExecutor;
|
||||
if (executor != null) {
|
||||
executor.execute(() -> processCommand(command));
|
||||
}
|
||||
}
|
||||
|
||||
private void processCommand(AM43Command command) {
|
||||
try {
|
||||
currentCommand = command;
|
||||
if (device.getConnectionState() != ConnectionState.CONNECTED) {
|
||||
logger.debug("Unable to send command {} to device {}: not connected", command, device.getAddress());
|
||||
command.setState(AM43Command.State.FAILED);
|
||||
return;
|
||||
}
|
||||
if (!resolved) {
|
||||
logger.debug("Unable to send command {} to device {}: services not resolved", command,
|
||||
device.getAddress());
|
||||
command.setState(AM43Command.State.FAILED);
|
||||
return;
|
||||
}
|
||||
BluetoothCharacteristic characteristic = device.getCharacteristic(AM43BindingConstants.CHARACTERISTIC_UUID);
|
||||
if (characteristic == null) {
|
||||
logger.warn("Unable to execute {}. Characteristic '{}' could not be found.", command,
|
||||
AM43BindingConstants.CHARACTERISTIC_UUID);
|
||||
command.setState(AM43Command.State.FAILED);
|
||||
return;
|
||||
}
|
||||
// there is no consequence to calling this as much as we like
|
||||
device.enableNotifications(characteristic);
|
||||
|
||||
characteristic.setValue(command.getRequest());
|
||||
command.setState(AM43Command.State.ENQUEUED);
|
||||
device.writeCharacteristic(characteristic);
|
||||
|
||||
if (!command.awaitStateChange(getAM43Config().commandTimeout, TimeUnit.MILLISECONDS,
|
||||
AM43Command.State.SUCCEEDED, AM43Command.State.FAILED)) {
|
||||
logger.debug("Command {} to device {} timed out", command, device.getAddress());
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// do nothing
|
||||
} finally {
|
||||
logger.trace("Command final state: {}", command.getState());
|
||||
currentCommand = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWriteComplete(BluetoothCharacteristic characteristic,
|
||||
BluetoothCompletionStatus status) {
|
||||
super.onCharacteristicWriteComplete(characteristic, status);
|
||||
|
||||
byte[] request = characteristic.getByteValue();
|
||||
|
||||
AM43Command command = currentCommand;
|
||||
|
||||
if (command != null) {
|
||||
if (!Arrays.equals(request, command.getRequest())) {
|
||||
logger.debug("Write completed for unknown command");
|
||||
return;
|
||||
}
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
command.setState(AM43Command.State.SENT);
|
||||
break;
|
||||
case ERROR:
|
||||
command.setState(AM43Command.State.FAILED);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No command found that matches request {}", HexUtils.bytesToHex(request));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicUpdate(BluetoothCharacteristic characteristic) {
|
||||
super.onCharacteristicUpdate(characteristic);
|
||||
|
||||
byte[] response = characteristic.getByteValue();
|
||||
|
||||
AM43Command command = currentCommand;
|
||||
if (command == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No command present to handle response {}", HexUtils.bytesToHex(response));
|
||||
}
|
||||
} else if (!command.handleResponse(scheduler, this, response)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Command {} could not handle response {}", command, HexUtils.bytesToHex(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
switch (channelUID.getId()) {
|
||||
case AM43BindingConstants.CHANNEL_ID_ELECTRIC:
|
||||
submitCommand(new GetBatteryLevelCommand());
|
||||
return;
|
||||
case AM43BindingConstants.CHANNEL_ID_LIGHT_LEVEL:
|
||||
submitCommand(new GetLightLevelCommand());
|
||||
return;
|
||||
case AM43BindingConstants.CHANNEL_ID_POSITION:
|
||||
submitCommand(new GetPositionCommand());
|
||||
return;
|
||||
}
|
||||
submitCommand(new GetAllCommand());
|
||||
return;
|
||||
}
|
||||
switch (channelUID.getId()) {
|
||||
case AM43BindingConstants.CHANNEL_ID_POSITION:
|
||||
if (command instanceof PercentType) {
|
||||
MotorSettings settings = motorSettings;
|
||||
if (settings == null) {
|
||||
logger.warn("Cannot set position before settings have been received.");
|
||||
return;
|
||||
}
|
||||
if (!settings.isTopLimitSet() || !settings.isBottomLimitSet()) {
|
||||
logger.warn("Cannot set position of blinds. Top or bottom limits have not been set. "
|
||||
+ "Please configure manually.");
|
||||
return;
|
||||
}
|
||||
PercentType percent = (PercentType) command;
|
||||
int value = percent.intValue();
|
||||
if (getAM43Config().invertPosition) {
|
||||
value = 100 - value;
|
||||
}
|
||||
submitCommand(new SetPositionCommand(value));
|
||||
return;
|
||||
}
|
||||
if (command instanceof StopMoveType) {
|
||||
switch ((StopMoveType) command) {
|
||||
case STOP:
|
||||
submitCommand(new ControlCommand(ControlAction.STOP));
|
||||
return;
|
||||
case MOVE:
|
||||
// do nothing
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (command instanceof UpDownType) {
|
||||
switch ((UpDownType) command) {
|
||||
case UP:
|
||||
submitCommand(new ControlCommand(ControlAction.OPEN));
|
||||
return;
|
||||
case DOWN:
|
||||
submitCommand(new ControlCommand(ControlAction.CLOSE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case AM43BindingConstants.CHANNEL_ID_SPEED:
|
||||
if (command instanceof DecimalType) {
|
||||
MotorSettings settings = motorSettings;
|
||||
if (settings != null) {
|
||||
DecimalType speedType = (DecimalType) command;
|
||||
settings.setSpeed(speedType.intValue());
|
||||
submitCommand(new SetSettingsCommand(settings));
|
||||
} else {
|
||||
logger.warn("Cannot set Speed before setting have been received");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case AM43BindingConstants.CHANNEL_ID_DIRECTION:
|
||||
if (command instanceof StringType) {
|
||||
MotorSettings settings = motorSettings;
|
||||
if (settings != null) {
|
||||
settings.setDirection(Direction.valueOf(command.toString()));
|
||||
submitCommand(new SetSettingsCommand(settings));
|
||||
} else {
|
||||
logger.warn("Cannot set Direction before setting have been received");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case AM43BindingConstants.CHANNEL_ID_OPERATION_MODE:
|
||||
if (command instanceof StringType) {
|
||||
MotorSettings settings = motorSettings;
|
||||
if (settings != null) {
|
||||
settings.setOperationMode(OperationMode.valueOf(command.toString()));
|
||||
submitCommand(new SetSettingsCommand(settings));
|
||||
} else {
|
||||
logger.warn("Cannot set OperationMode before setting have been received");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
super.handleCommand(channelUID, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetLightLevelCommand command) {
|
||||
updateLightLevel(command.getLightLevel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetPositionCommand command) {
|
||||
updatePosition(command.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetSpeedCommand command) {
|
||||
getMotorSettings().setSpeed(command.getSpeed());
|
||||
updateSpeed(command.getSpeed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetAllCommand command) {
|
||||
motorSettings = new MotorSettings();
|
||||
|
||||
updateDirection(command.getDirection());
|
||||
updateOperationMode(command.getOperationMode());
|
||||
updateTopLimitSet(command.getTopLimitSet());
|
||||
updateBottomLimitSet(command.getBottomLimitSet());
|
||||
updateHasLightSensor(command.getHasLightSensor());
|
||||
updateSpeed(command.getSpeed());
|
||||
updatePosition(command.getPosition());
|
||||
updateLength(command.getLength());
|
||||
updateDiameter(command.getDiameter());
|
||||
updateType(command.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedResponse(GetBatteryLevelCommand command) {
|
||||
updateBatteryLevel(command.getBatteryLevel());
|
||||
}
|
||||
|
||||
private void updateDirection(Direction direction) {
|
||||
getMotorSettings().setDirection(direction);
|
||||
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_DIRECTION, new StringType(direction.toString()));
|
||||
}
|
||||
|
||||
private void updateOperationMode(OperationMode opMode) {
|
||||
getMotorSettings().setOperationMode(opMode);
|
||||
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_OPERATION_MODE, new StringType(opMode.toString()));
|
||||
}
|
||||
|
||||
private void updateTopLimitSet(boolean bitValue) {
|
||||
getMotorSettings().setTopLimitSet(bitValue);
|
||||
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_TOP_LIMIT_SET, OnOffType.from(bitValue));
|
||||
}
|
||||
|
||||
private void updateBottomLimitSet(boolean bitValue) {
|
||||
getMotorSettings().setBottomLimitSet(bitValue);
|
||||
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_BOTTOM_LIMIT_SET, OnOffType.from(bitValue));
|
||||
}
|
||||
|
||||
private void updateHasLightSensor(boolean bitValue) {
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_HAS_LIGHT_SENSOR, OnOffType.from(bitValue));
|
||||
}
|
||||
|
||||
private void updateSpeed(int value) {
|
||||
getMotorSettings().setSpeed(value);
|
||||
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_SPEED, new DecimalType(value));
|
||||
}
|
||||
|
||||
private void updatePosition(int value) {
|
||||
if (value >= 0 && value <= 100) {
|
||||
int percentValue = value;
|
||||
if (getAM43Config().invertPosition) {
|
||||
percentValue = 100 - percentValue;
|
||||
}
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_POSITION, new PercentType(percentValue));
|
||||
} else {
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_POSITION, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLength(int value) {
|
||||
getMotorSettings().setLength(value);
|
||||
|
||||
QuantityType<Length> lengthType = QuantityType.valueOf(value, MetricPrefix.MILLI(SIUnits.METRE));
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_LENGTH, lengthType);
|
||||
}
|
||||
|
||||
private void updateDiameter(int value) {
|
||||
getMotorSettings().setDiameter(value);
|
||||
|
||||
QuantityType<Length> diameter = QuantityType.valueOf(value, MetricPrefix.MILLI(SIUnits.METRE));
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_DIAMETER, diameter);
|
||||
}
|
||||
|
||||
private void updateType(int value) {
|
||||
getMotorSettings().setType(value);
|
||||
|
||||
DecimalType type = new DecimalType(value);
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_TYPE, type);
|
||||
}
|
||||
|
||||
private void updateLightLevel(int value) {
|
||||
DecimalType lightLevel = new DecimalType(value);
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_LIGHT_LEVEL, lightLevel);
|
||||
}
|
||||
|
||||
private void updateBatteryLevel(int value) {
|
||||
if (value >= 0 && value <= 100) {
|
||||
DecimalType deviceElectric = new DecimalType(value & 0xFF);
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_ELECTRIC, deviceElectric);
|
||||
} else {
|
||||
updateStateIfLinked(AM43BindingConstants.CHANNEL_ID_ELECTRIC, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStateIfLinked(String channelUID, State state) {
|
||||
if (isLinked(channelUID)) {
|
||||
updateState(channelUID, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
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 AM43HandlerFactory} is responsible for creating AM43Handler instances
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.am43")
|
||||
public class AM43HandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.singleton(AM43BindingConstants.THING_TYPE_AM43);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (AM43BindingConstants.THING_TYPE_AM43.equals(thingTypeUID)) {
|
||||
return new AM43Handler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link AM43Command} provides basic functionality that all commands for the AM43 share.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AM43Command {
|
||||
|
||||
private static final byte[] REQUEST_PREFIX = { 0, (byte) 0xFF, 0, 0 };
|
||||
private static final byte HEADER_PREFIX = (byte) 0x9a;
|
||||
|
||||
private final Lock stateLock = new ReentrantLock();
|
||||
|
||||
private final Condition stateCondition = stateLock.newCondition();
|
||||
|
||||
private volatile State state;
|
||||
|
||||
private byte header;
|
||||
|
||||
private byte[] data;
|
||||
|
||||
private byte @Nullable [] response;
|
||||
|
||||
public AM43Command(byte commandHeader, byte... data) {
|
||||
this.header = commandHeader;
|
||||
this.data = data;
|
||||
this.state = State.NEW;
|
||||
}
|
||||
|
||||
public enum State {
|
||||
NEW,
|
||||
ENQUEUED,
|
||||
SENT,
|
||||
SUCCEEDED,
|
||||
FAILED
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current state of the command.
|
||||
*
|
||||
* @return current state
|
||||
*/
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets state of the command.
|
||||
*
|
||||
* @param state new state
|
||||
*/
|
||||
public void setState(State state) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
this.state = state;
|
||||
stateCondition.signalAll();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean awaitStateChange(long timeout, TimeUnit unit, State... expectedStates) throws InterruptedException {
|
||||
stateLock.lock();
|
||||
try {
|
||||
long nanosTimeout = unit.toNanos(timeout);
|
||||
while (!isInAnyState(expectedStates)) {
|
||||
if (nanosTimeout <= 0L) {
|
||||
return false;
|
||||
}
|
||||
nanosTimeout = stateCondition.awaitNanos(nanosTimeout);
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isInAnyState(State[] acceptedStates) {
|
||||
for (State acceptedState : acceptedStates) {
|
||||
if (acceptedState == state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public byte getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public static byte getRequestHeader(byte[] request) {
|
||||
return request[REQUEST_PREFIX.length + 1];
|
||||
}
|
||||
|
||||
public static byte getResponseHeader(byte[] response) {
|
||||
return response[1];
|
||||
}
|
||||
|
||||
public byte[] getRequest() {
|
||||
byte[] value = ArrayUtils.EMPTY_BYTE_ARRAY;
|
||||
value = ArrayUtils.add(value, HEADER_PREFIX);
|
||||
value = ArrayUtils.add(value, header);
|
||||
value = ArrayUtils.add(value, (byte) data.length);
|
||||
value = ArrayUtils.addAll(value, data);
|
||||
value = ArrayUtils.add(value, createChecksum(value));
|
||||
return ArrayUtils.addAll(REQUEST_PREFIX, value);
|
||||
}
|
||||
|
||||
protected byte createChecksum(byte[] data) {
|
||||
// this is a basic checksum
|
||||
byte crc = data[0];
|
||||
for (int i = 1; i < data.length; i++) {
|
||||
crc ^= data[i];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
public boolean handleResponse(Executor executor, ResponseListener listener, byte @Nullable [] response) {
|
||||
if (response == null || response.length < minResponseSize()) {
|
||||
return false;
|
||||
}
|
||||
if (getResponseHeader(response) != header) {
|
||||
return false;
|
||||
}
|
||||
this.response = response;
|
||||
setState(State.SUCCEEDED);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int minResponseSize() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public byte[] getResponse() {
|
||||
byte[] ret = response;
|
||||
if (ret == null) {
|
||||
throw new IllegalStateException("Response has yet to be received");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.ControlAction;
|
||||
|
||||
/**
|
||||
* The {@link ControlCommand} is used for basic UP, DOWN, and STOP commands.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ControlCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0x0a;
|
||||
|
||||
public ControlCommand(ControlAction action) {
|
||||
super(COMMAND, action.getCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.Direction;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.OperationMode;
|
||||
|
||||
/**
|
||||
* The {@link GetAllCommand} gets the bulk of the settings on AM43.
|
||||
* A GetAllCommand request actually returns several responses. But we are
|
||||
* only interested in the device settings response (which luckily has the same command header as the request).
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetAllCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0xa7;
|
||||
|
||||
public GetAllCommand() {
|
||||
super(COMMAND, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleResponse(Executor executor, ResponseListener listener, byte @Nullable [] response) {
|
||||
if (super.handleResponse(executor, listener, response)) {
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return Direction.valueOf((getResponse()[3] & 1) > 0);
|
||||
}
|
||||
|
||||
public OperationMode getOperationMode() {
|
||||
return OperationMode.valueOf((getResponse()[3] & 2) > 0);
|
||||
}
|
||||
|
||||
public boolean getTopLimitSet() {
|
||||
return (getResponse()[3] & 4) > 0;
|
||||
}
|
||||
|
||||
public boolean getBottomLimitSet() {
|
||||
return (getResponse()[3] & 8) > 0;
|
||||
}
|
||||
|
||||
public boolean getHasLightSensor() {
|
||||
return (getResponse()[3] & 16) > 0;
|
||||
}
|
||||
|
||||
public int getSpeed() {
|
||||
return getResponse()[4];
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return getResponse()[5];
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return getResponse()[6] << 8 | getResponse()[7];
|
||||
}
|
||||
|
||||
public int getDiameter() {
|
||||
return getResponse()[8];
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return Math.abs(getResponse()[9] >> 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minResponseSize() {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link GetBatteryLevelCommand} is used to get the current battery level of the AM43.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetBatteryLevelCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0xa2;
|
||||
|
||||
public GetBatteryLevelCommand() {
|
||||
super(COMMAND, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleResponse(Executor executor, ResponseListener listener, byte @Nullable [] response) {
|
||||
if (super.handleResponse(executor, listener, response)) {
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getBatteryLevel() {
|
||||
return getResponse()[7];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minResponseSize() {
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link GetLightLevelCommand} is used to get the current light sensor reading.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetLightLevelCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0xaa;
|
||||
|
||||
public GetLightLevelCommand() {
|
||||
super(COMMAND, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleResponse(Executor executor, ResponseListener listener, byte @Nullable [] response) {
|
||||
if (super.handleResponse(executor, listener, response)) {
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getLightLevel() {
|
||||
return getResponse()[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minResponseSize() {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link GetPositionCommand} is used to get the current position of the blinds, ranging from 0 to 100.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetPositionCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0xa1;
|
||||
|
||||
public GetPositionCommand() {
|
||||
super(COMMAND, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleResponse(Executor executor, ResponseListener listener, byte @Nullable [] response) {
|
||||
if (super.handleResponse(executor, listener, response)) {
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return getResponse()[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minResponseSize() {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link GetSpeedCommand} gets the current speed setting of the motor.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GetSpeedCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0xa3;
|
||||
|
||||
public GetSpeedCommand() {
|
||||
super(COMMAND, (byte) 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleResponse(Executor executor, ResponseListener listener, byte @Nullable [] response) {
|
||||
if (super.handleResponse(executor, listener, response)) {
|
||||
executor.execute(() -> listener.receivedResponse(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getSpeed() {
|
||||
return getResponse()[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minResponseSize() {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SetPositionCommand} sets the position of the motor, ranging from 0 to 100.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResetLimitsCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0x22;
|
||||
|
||||
public ResetLimitsCommand() {
|
||||
super(COMMAND, (byte) 0, (byte) 0, (byte) 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ResponseListener} used a callback for handling the responses of certain commands.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface ResponseListener {
|
||||
|
||||
public void receivedResponse(GetBatteryLevelCommand command);
|
||||
|
||||
public void receivedResponse(GetAllCommand command);
|
||||
|
||||
public void receivedResponse(GetLightLevelCommand command);
|
||||
|
||||
public void receivedResponse(GetPositionCommand command);
|
||||
|
||||
public void receivedResponse(GetSpeedCommand command);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SetPositionCommand} sets the position of the motor, ranging from 0 to 100.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SetPositionCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0x0d;
|
||||
|
||||
public SetPositionCommand(int position) {
|
||||
super(COMMAND, (byte) position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.MotorSettings;
|
||||
|
||||
/**
|
||||
* The {@link SetSettingsCommand} sets the motor settings in bulk. There isn't a way to set them individually so before
|
||||
* you can use this command you need to retrieve the existing commands with a {@link GetAllCommand}
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SetSettingsCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0x11;
|
||||
|
||||
public SetSettingsCommand(MotorSettings settings) {
|
||||
super(COMMAND, createContent(settings));
|
||||
}
|
||||
|
||||
private static byte[] createContent(MotorSettings motorSettings) {
|
||||
@SuppressWarnings("null")
|
||||
int direction = motorSettings.getDirection().toByte();
|
||||
@SuppressWarnings("null")
|
||||
int operationMode = motorSettings.getOperationMode().toByte();
|
||||
int deviceType = motorSettings.getType();
|
||||
int deviceLength = motorSettings.getLength();
|
||||
int deviceSpeed = motorSettings.getSpeed();
|
||||
int deviceDiameter = motorSettings.getDiameter();
|
||||
|
||||
int dataHead = ((direction & 1) << 1) | ((operationMode & 1) << 2) | (deviceType << 4);
|
||||
|
||||
return new byte[] { (byte) dataHead, (byte) deviceSpeed, 0, (byte) ((deviceLength & 0xFF00) >> 8),
|
||||
(byte) (deviceLength & 0xFF), (byte) deviceDiameter };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.command;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SetTimeCommand} set the current time to the motor, which it uses for scheduled activation.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SetTimeCommand extends AM43Command {
|
||||
|
||||
private static final byte COMMAND = (byte) 0x14;
|
||||
|
||||
public SetTimeCommand() {
|
||||
super(COMMAND, createContent());
|
||||
}
|
||||
|
||||
private static byte[] createContent() {
|
||||
Calendar instance = Calendar.getInstance();
|
||||
int hour = instance.get(Calendar.HOUR);
|
||||
if (instance.get(Calendar.AM_PM) != 0) {
|
||||
hour += 12;
|
||||
}
|
||||
int minute = instance.get(Calendar.MINUTE);
|
||||
int second = instance.get(Calendar.SECOND);
|
||||
int dayOfWeek = instance.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
return new byte[] { (byte) dayOfWeek, (byte) hour, (byte) minute, (byte) second };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.data;
|
||||
|
||||
/**
|
||||
* The {@link ControlAction} list possible controls actions that can be sent through
|
||||
* {@link org.openhab.binding.bluetooth.am43.internal.command.ControlCommand}
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
public enum ControlAction {
|
||||
CLOSE(0xee),
|
||||
OPEN(0xdd),
|
||||
STOP(0xcc);
|
||||
|
||||
private byte code;
|
||||
|
||||
private ControlAction(int code) {
|
||||
this.code = (byte) code;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.data;
|
||||
|
||||
/**
|
||||
* This is an enum representing possible motor direction settings
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
public enum Direction {
|
||||
Forward(0x1),
|
||||
Reverse(0x0);
|
||||
|
||||
private byte value;
|
||||
|
||||
private Direction(int value) {
|
||||
this.value = (byte) value;
|
||||
}
|
||||
|
||||
public byte toByte() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Direction valueOf(boolean bitValue) {
|
||||
return bitValue ? Forward : Reverse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.data;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link MotorSettings} is contains the settings which are sent in batch to
|
||||
* {@link org.openhab.binding.bluetooth.am43.internal.command.SetSettingsCommand}.
|
||||
* These settings cannot be changed individually and must be sent together in the same command.
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MotorSettings {
|
||||
|
||||
@Nullable
|
||||
private Direction direction;
|
||||
@Nullable
|
||||
private OperationMode operationMode;
|
||||
|
||||
private boolean topLimitSet;
|
||||
|
||||
private boolean bottomLimitSet;
|
||||
|
||||
private int type = 0;
|
||||
// speed by rpm
|
||||
private int speed = 0;
|
||||
// lengh in mm
|
||||
private int length;
|
||||
// diameter in mm
|
||||
private int diameter;
|
||||
|
||||
@Nullable
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(Direction direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public OperationMode getOperationMode() {
|
||||
return operationMode;
|
||||
}
|
||||
|
||||
public void setOperationMode(OperationMode operationMode) {
|
||||
this.operationMode = operationMode;
|
||||
}
|
||||
|
||||
public boolean isTopLimitSet() {
|
||||
return topLimitSet;
|
||||
}
|
||||
|
||||
public void setTopLimitSet(boolean value) {
|
||||
this.topLimitSet = value;
|
||||
}
|
||||
|
||||
public boolean isBottomLimitSet() {
|
||||
return bottomLimitSet;
|
||||
}
|
||||
|
||||
public void setBottomLimitSet(boolean value) {
|
||||
this.bottomLimitSet = value;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public void setSpeed(int speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public int getDiameter() {
|
||||
return diameter;
|
||||
}
|
||||
|
||||
public void setDiameter(int diameter) {
|
||||
this.diameter = diameter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43.internal.data;
|
||||
|
||||
/**
|
||||
* This is an enum representing possible motor modes settings
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
public enum OperationMode {
|
||||
Inching(0x1),
|
||||
Continuous(0x0);
|
||||
|
||||
private byte value;
|
||||
|
||||
private OperationMode(int value) {
|
||||
this.value = (byte) value;
|
||||
}
|
||||
|
||||
public byte toByte() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static OperationMode valueOf(boolean bitValue) {
|
||||
return bitValue ? Inching : Continuous;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="bluetooth"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="am43">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="roaming"/>
|
||||
<bridge-type-ref id="bluegiga"/>
|
||||
<bridge-type-ref id="bluez"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>AM43 Blind Drive Motor</label>
|
||||
<description>A AM43 Blind Drive Motor</description>
|
||||
<category>Blinds</category>
|
||||
|
||||
<channels>
|
||||
<channel id="rssi" typeId="rssi"/>
|
||||
<channel id="direction" typeId="am43_direction"/>
|
||||
<channel id="topLimitSet" typeId="am43_topLimitSet"/>
|
||||
<channel id="bottomLimitSet" typeId="am43_bottomLimitSet"/>
|
||||
<channel id="hasLightSensor" typeId="am43_hasLightSensor"/>
|
||||
<channel id="operationMode" typeId="am43_operationMode"/>
|
||||
<channel id="speed" typeId="am43_speed"/>
|
||||
<channel id="electric" typeId="system.battery-level"/>
|
||||
<channel id="position" typeId="am43_position"/>
|
||||
<channel id="length" typeId="am43_length"/>
|
||||
<channel id="diameter" typeId="am43_diameter"/>
|
||||
<channel id="type" typeId="am43_type"/>
|
||||
<channel id="lightLevel" typeId="am43_lightLevel"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="address" type="text">
|
||||
<label>Address</label>
|
||||
<description>Bluetooth address in XX:XX:XX:XX:XX:XX format</description>
|
||||
</parameter>
|
||||
<parameter name="invertPosition" type="boolean">
|
||||
<label>Invert Position</label>
|
||||
<description>Inverts the blinds percentages such that 0 becomes 100 and 100 becomes 0</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" min="1" max="86400" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<advanced>true</advanced>
|
||||
<description>Refresh interval for battery and light sensor data (in seconds). This could impact battery lifetime</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
<parameter name="commandTimeout" type="integer" unit="ms">
|
||||
<label>Command Timeout</label>
|
||||
<advanced>true</advanced>
|
||||
<description>The amount of time, in milliseconds, a command should take before it times out.</description>
|
||||
<default>1000</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="am43_direction">
|
||||
<item-type>String</item-type>
|
||||
<label>Direction</label>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="Forward">Forward</option>
|
||||
<option value="Reverse">Reverse</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_topLimitSet" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Top Limit is Set</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_bottomLimitSet" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Bottom Limit is Set</label>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_hasLightSensor" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Light Sensor Present</label>
|
||||
<description>Whether or not a light sensor is attached to the motor</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_operationMode" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Operation Mode</label>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="Inching">Inching</option>
|
||||
<option value="Continuous">Continuous</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_speed" advanced="true">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Speed</label>
|
||||
<description>The speed value in RPMs set for this motor</description>
|
||||
<state readOnly="false" pattern="%d RPM"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_position">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Position</label>
|
||||
<description>The percent value of the blind position</description>
|
||||
<state readOnly="false" pattern="%d %%" min="0" max="100"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_length" advanced="true">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Length</label>
|
||||
<description>The distance the blinds travel from full open and close (not really important)</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_diameter" advanced="true">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Diameter</label>
|
||||
<description>The diameter of the pulley of this motor (not really important)</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_type" advanced="true">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Device Type</label>
|
||||
<description>The type of blinds this motor is operating. (not really important)</description>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="am43_lightLevel">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Light Level</label>
|
||||
<description>Light level detected by the solar sensor. Will range from 0-10</description>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.bluetooth.am43;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.ControlCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.command.GetAllCommand;
|
||||
import org.openhab.binding.bluetooth.am43.internal.data.ControlAction;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Connor Petty - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CommandTest {
|
||||
|
||||
@Test
|
||||
public void findAllCommandTest() {
|
||||
byte[] expected = HexUtils.hexToBytes("00ff00009aa701013d");
|
||||
byte[] actual = new GetAllCommand().getRequest();
|
||||
Assert.assertArrayEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlStopCommandTest() {
|
||||
byte[] expected = HexUtils.hexToBytes("00ff00009a0a01cc5d");
|
||||
byte[] actual = new ControlCommand(ControlAction.STOP).getRequest();
|
||||
|
||||
Assert.assertArrayEquals(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlOpenCommandTest() {
|
||||
byte[] expected = HexUtils.hexToBytes("00ff00009a0a01dd4c");
|
||||
byte[] actual = new ControlCommand(ControlAction.OPEN).getRequest();
|
||||
|
||||
Assert.assertArrayEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user