[enocean] Improved device discovery and added SMACK capability (#10157)
* Added SMACK teach in * Teached in devices can be teach out on a repeated teach in * Improved detection of RPS devices, device types can be better distinguished now * Bugfixes for discovery fallback to GenericThings * Responses to message requests are send automatically now, no need for linking SEND_COMMAND channel Fixes #10156 Signed-off-by: Daniel Weber <uni@fruggy.de>
This commit is contained in:
parent
a9f440dba2
commit
fd1c96677e
@ -126,7 +126,21 @@ The corresponding channels are created dynamically, too.
|
||||
If the actuator supports UTE teach-in, the corresponding thing can be created and paired automatically.
|
||||
First you have to **start the discovery scan for a gateway**.
|
||||
Then press the teach-in button of the actuator.
|
||||
If the EEP of the actuator is known, the binding sends an UTE teach-in response with a new SenderId and creates a new thing with its channels.
|
||||
If the EEP of the actuator is known, the binding sends an UTE teach-in response with a new SenderId and creates a new thing with its channels.
|
||||
|
||||
This binding supports so called smart acknowlegde (SMACK) devices too.
|
||||
Before you can pair a SMACK device you have to configure your gateway bridge as a SMACK postmaster.
|
||||
If this option is enabled you can pair up to 20 SMACK devices with your gateway.
|
||||
|
||||
Communication between your gateway and a SMACK device is handled through mailboxes.
|
||||
A mailbox is created for each paired SMACK device and deleted after teach out.
|
||||
You can see the paired SMACK devices and their mailbox index in the gateway properties.
|
||||
SMACK devices send periodically status updates followed by a response request.
|
||||
Whenever such a request is received a `requestAnswer` event is triggered for channel `statusRequestEvent`.
|
||||
Afterwards you have 100ms time to recalculate your items states and update them.
|
||||
A message with the updated item states is built, put into the corresponding mailbox and automatically sent upon request of the device.
|
||||
Pairing and unpairing can be done through a discovery scan.
|
||||
The corresponding thing of an unpaired device gets disabled, you have to delete it manually if you want to.
|
||||
|
||||
If the actuator does not support UTE teach-ins, you have to create, configure and choose the right EEP of the thing manually.
|
||||
It is important to link the teach-in channel of this thing to a switch item.
|
||||
@ -158,6 +172,8 @@ If you change the SenderId of your thing, you have to pair again the thing with
|
||||
| | espVersion | ESP Version of gateway | ESP3, ESP2 |
|
||||
| | rs485 | If gateway is directly connected to a RS485 bus the BaseId is set to 0x00 | true, false
|
||||
| | rs485BaseId | Override BaseId 0x00 if your bus contains a telegram duplicator (FTD14 for ex) | 4 byte hex value |
|
||||
| | enableSmack | Enables SMACK pairing and handling of SMACK messages | true, false |
|
||||
| | sendTeachOuts | Defines if a repeated teach in request should be answered with a learned in or teach out response | true, false |
|
||||
| pushButton | receivingEEPId | EEP used for receiving msg | F6_01_01, D2_03_0A |
|
||||
| | enoceanId | EnOceanId of device this thing belongs to | hex value as string |
|
||||
| rockerSwitch | receivingEEPId | | F6_02_01, F6_02_02 |
|
||||
@ -300,6 +316,7 @@ The channels of a thing are determined automatically based on the chosen EEP.
|
||||
| rssi | Number | Received Signal Strength Indication (dBm) of last received message |
|
||||
| repeatCount | Number | Number of repeaters involved in the transmission of the telegram |
|
||||
| lastReceived | DateTime | Date and time the last telegram was received |
|
||||
| statusRequestEvent | Trigger | Emits event 'requestAnswer' |
|
||||
|
||||
Items linked to bi-directional actuators (actuator sends status messages back) should always disable the `autoupdate`.
|
||||
This is especially true for Eltako rollershutter, as their position is calculated out of the current position and the moving time.
|
||||
|
||||
@ -177,7 +177,7 @@ public class EnOceanBindingConstants {
|
||||
public static final String CHANNEL_WAKEUPCYCLE = "wakeUpCycle";
|
||||
public static final String CHANNEL_SERVICECOMMAND = "serviceCommand";
|
||||
public static final String CHANNEL_STATUS_REQUEST_EVENT = "statusRequestEvent";
|
||||
public static final String CHANNEL_SEND_COMMAND = "sendCommand";
|
||||
public static final String VIRTUALCHANNEL_SEND_COMMAND = "sendCommand";
|
||||
|
||||
public static final String CHANNEL_VENTILATIONOPERATIONMODE = "ventilationOperationMode";
|
||||
public static final String CHANNEL_FIREPLACESAFETYMODE = "fireplaceSafetyMode";
|
||||
@ -293,7 +293,8 @@ public class EnOceanBindingConstants {
|
||||
Map.entry(CHANNEL_INDOORAIRANALYSIS,
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_INDOORAIRANALYSIS),
|
||||
CoreItemFactory.STRING)),
|
||||
Map.entry(CHANNEL_SETPOINT,
|
||||
Map.entry(
|
||||
CHANNEL_SETPOINT,
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SETPOINT),
|
||||
CoreItemFactory.NUMBER)),
|
||||
Map.entry(CHANNEL_CONTACT,
|
||||
@ -444,13 +445,6 @@ public class EnOceanBindingConstants {
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SERVICECOMMAND),
|
||||
CoreItemFactory.NUMBER)),
|
||||
|
||||
Map.entry(CHANNEL_STATUS_REQUEST_EVENT,
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_STATUS_REQUEST_EVENT), null,
|
||||
"", false, true)),
|
||||
Map.entry(CHANNEL_SEND_COMMAND,
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_SEND_COMMAND),
|
||||
CoreItemFactory.SWITCH)),
|
||||
|
||||
Map.entry(CHANNEL_VENTILATIONOPERATIONMODE,
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_VENTILATIONOPERATIONMODE),
|
||||
CoreItemFactory.STRING)),
|
||||
@ -527,6 +521,10 @@ public class EnOceanBindingConstants {
|
||||
CoreItemFactory.NUMBER + ItemUtil.EXTENSION_SEPARATOR
|
||||
+ Dimensionless.class.getSimpleName())),
|
||||
|
||||
Map.entry(CHANNEL_STATUS_REQUEST_EVENT,
|
||||
new EnOceanChannelDescription(new ChannelTypeUID(BINDING_ID, CHANNEL_STATUS_REQUEST_EVENT), null,
|
||||
"", false, true)),
|
||||
|
||||
Map.entry(CHANNEL_REPEATERMODE, new EnOceanChannelDescription(
|
||||
new ChannelTypeUID(BINDING_ID, CHANNEL_REPEATERMODE), CoreItemFactory.STRING)));
|
||||
|
||||
@ -536,11 +534,8 @@ public class EnOceanBindingConstants {
|
||||
public static final String REPEATERMODE_LEVEL_2 = "LEVEL2";
|
||||
|
||||
// Bridge config properties
|
||||
public static final String SENDERID = "senderId";
|
||||
public static final String PATH = "path";
|
||||
public static final String HOST = "host";
|
||||
public static final String RS485 = "rs485";
|
||||
public static final String NEXTSENDERID = "nextSenderId";
|
||||
public static final String PARAMETER_NEXT_SENDERID = "nextSenderId";
|
||||
|
||||
// Bridge properties
|
||||
public static final String PROPERTY_BASE_ID = "Base ID";
|
||||
@ -551,13 +546,12 @@ public class EnOceanBindingConstants {
|
||||
public static final String PROPERTY_DESCRIPTION = "Description";
|
||||
|
||||
// Thing properties
|
||||
public static final String PROPERTY_ENOCEAN_ID = "enoceanId";
|
||||
public static final String PROPERTY_SENDINGENOCEAN_ID = "SendingEnoceanId";
|
||||
|
||||
// Thing config parameter
|
||||
public static final String PARAMETER_SENDERIDOFFSET = "senderIdOffset";
|
||||
public static final String PARAMETER_SENDINGEEPID = "sendingEEPId";
|
||||
public static final String PARAMETER_RECEIVINGEEPID = "receivingEEPId";
|
||||
public static final String PARAMETER_EEPID = "eepId";
|
||||
|
||||
public static final String PARAMETER_BROADCASTMESSAGES = "broadcastMessages";
|
||||
public static final String PARAMETER_ENOCEANID = "enoceanId";
|
||||
|
||||
@ -28,6 +28,7 @@ import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingManager;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
@ -60,6 +61,9 @@ public class EnOceanHandlerFactory extends BaseThingHandlerFactory {
|
||||
@Reference
|
||||
ItemChannelLinkRegistry itemChannelLinkRegistry;
|
||||
|
||||
@Reference
|
||||
ThingManager thingManager;
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
@ -96,7 +100,7 @@ public class EnOceanHandlerFactory extends BaseThingHandlerFactory {
|
||||
}
|
||||
|
||||
private void registerDeviceDiscoveryService(EnOceanBridgeHandler handler) {
|
||||
EnOceanDeviceDiscoveryService discoveryService = new EnOceanDeviceDiscoveryService(handler);
|
||||
EnOceanDeviceDiscoveryService discoveryService = new EnOceanDeviceDiscoveryService(handler, thingManager);
|
||||
discoveryService.activate();
|
||||
this.discoveryServiceRegs.put(handler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
|
||||
@ -19,7 +19,7 @@ package org.openhab.binding.enocean.internal.config;
|
||||
public class EnOceanActuatorConfig extends EnOceanBaseConfig {
|
||||
|
||||
public int channel;
|
||||
public int senderIdOffset = -1;
|
||||
public Integer senderIdOffset = null;
|
||||
public String manufacturerId;
|
||||
public String teachInType;
|
||||
|
||||
|
||||
@ -17,15 +17,23 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.EMPTY
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EnOceanBaseConfig {
|
||||
/**
|
||||
* EnOceanId of the physical device
|
||||
*/
|
||||
public String enoceanId;
|
||||
|
||||
/**
|
||||
* EEP used/send by physical device
|
||||
*/
|
||||
public List<String> receivingEEPId = new ArrayList<>();
|
||||
public boolean receivingSIGEEP = false;
|
||||
|
||||
|
||||
@ -46,10 +46,16 @@ public class EnOceanBridgeConfig {
|
||||
public boolean rs485;
|
||||
public String rs485BaseId;
|
||||
|
||||
public int nextSenderId = 0;
|
||||
public Integer nextSenderId;
|
||||
|
||||
public boolean enableSmack;
|
||||
public boolean sendTeachOuts;
|
||||
|
||||
public EnOceanBridgeConfig() {
|
||||
espVersion = "ESP3";
|
||||
sendTeachOuts = false;
|
||||
enableSmack = true;
|
||||
nextSenderId = null;
|
||||
}
|
||||
|
||||
public ESPVersion getESPVersion() {
|
||||
|
||||
@ -12,12 +12,19 @@
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.config;
|
||||
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public class EnOceanChannelTransformationConfig {
|
||||
public class EnOceanChannelTransformationConfig extends Configuration {
|
||||
|
||||
public String transformationType;
|
||||
public String transformationFunction;
|
||||
|
||||
public EnOceanChannelTransformationConfig() {
|
||||
put("transformationType", "");
|
||||
put("transformationFunction", "");
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,9 +25,14 @@ import org.openhab.binding.enocean.internal.handler.EnOceanBridgeHandler;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
|
||||
import org.openhab.binding.enocean.internal.transceiver.PacketListener;
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage;
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.SMACKTeachInResponse;
|
||||
import org.openhab.binding.enocean.internal.transceiver.TeachInListener;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingManager;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
@ -39,15 +44,16 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
|
||||
public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements PacketListener {
|
||||
public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService implements TeachInListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(EnOceanDeviceDiscoveryService.class);
|
||||
|
||||
private EnOceanBridgeHandler bridgeHandler;
|
||||
private ThingManager thingManager;
|
||||
|
||||
public EnOceanDeviceDiscoveryService(EnOceanBridgeHandler bridgeHandler) {
|
||||
public EnOceanDeviceDiscoveryService(EnOceanBridgeHandler bridgeHandler, ThingManager thingManager) {
|
||||
super(null, 60, false);
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
this.thingManager = thingManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,73 +107,140 @@ public class EnOceanDeviceDiscoveryService extends AbstractDiscoveryService impl
|
||||
return;
|
||||
}
|
||||
|
||||
String enoceanId = HexUtils.bytesToHex(eep.getSenderId());
|
||||
|
||||
bridgeHandler.getThing().getThings().stream()
|
||||
.filter(t -> t.getConfiguration().getProperties().getOrDefault(PARAMETER_ENOCEANID, EMPTYENOCEANID)
|
||||
.toString().equals(enoceanId))
|
||||
.findFirst().ifPresentOrElse(t -> {
|
||||
// If repeated learn is not allowed => send teach out
|
||||
// otherwise do nothing
|
||||
if (bridgeHandler.sendTeachOuts()) {
|
||||
sendTeachOutResponse(msg, enoceanId, t);
|
||||
thingManager.setEnabled(t.getUID(), false);
|
||||
}
|
||||
}, () -> {
|
||||
Integer senderIdOffset = null;
|
||||
boolean broadcastMessages = true;
|
||||
|
||||
// check for bidirectional communication => do not use broadcast in this case
|
||||
if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0]
|
||||
& UTEResponse.CommunicationType_MASK) == UTEResponse.CommunicationType_MASK) {
|
||||
broadcastMessages = false;
|
||||
}
|
||||
|
||||
if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] & UTEResponse.ResponseNeeded_MASK) == 0) {
|
||||
// if ute => send response if needed
|
||||
logger.debug("Sending UTE response to {}", enoceanId);
|
||||
senderIdOffset = sendTeachInResponse(msg, enoceanId);
|
||||
if (senderIdOffset == null) {
|
||||
return;
|
||||
}
|
||||
} else if ((eep instanceof _4BSMessage) && ((_4BSMessage) eep).isTeachInVariation3Supported()) {
|
||||
// if 4BS teach in variation 3 => send response
|
||||
logger.debug("Sending 4BS teach in variation 3 response to {}", enoceanId);
|
||||
senderIdOffset = sendTeachInResponse(msg, enoceanId);
|
||||
if (senderIdOffset == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
createDiscoveryResult(eep, broadcastMessages, senderIdOffset);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventReceived(EventMessage event) {
|
||||
if (event.getEventMessageType() == EventMessageType.SA_CONFIRM_LEARN) {
|
||||
EEP eep = EEPFactory.buildEEPFromTeachInSMACKEvent(event);
|
||||
if (eep == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SMACKTeachInResponse response = EEPFactory.buildResponseFromSMACKTeachIn(event,
|
||||
bridgeHandler.sendTeachOuts());
|
||||
if (response != null) {
|
||||
bridgeHandler.sendMessage(response, null);
|
||||
|
||||
if (response.isTeachIn()) {
|
||||
// SenderIdOffset will be determined during Thing init
|
||||
createDiscoveryResult(eep, false, -1);
|
||||
} else if (response.isTeachOut()) {
|
||||
// disable already teached in thing
|
||||
bridgeHandler.getThing().getThings().stream()
|
||||
.filter(t -> t.getConfiguration().getProperties()
|
||||
.getOrDefault(PARAMETER_ENOCEANID, EMPTYENOCEANID).toString()
|
||||
.equals(HexUtils.bytesToHex(eep.getSenderId())))
|
||||
.findFirst().ifPresentOrElse(t -> {
|
||||
thingManager.setEnabled(t.getUID(), false);
|
||||
logger.info("Disable thing with id {}", t.getUID());
|
||||
}, () -> {
|
||||
logger.info("Thing for EnOceanId {} already deleted",
|
||||
HexUtils.bytesToHex(eep.getSenderId()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Integer sendTeachInResponse(ERP1Message msg, String enoceanId) {
|
||||
// get new sender Id
|
||||
Integer offset = bridgeHandler.getNextSenderId(enoceanId);
|
||||
if (offset != null) {
|
||||
byte[] newSenderId = bridgeHandler.getBaseId();
|
||||
newSenderId[3] += offset;
|
||||
|
||||
// send response
|
||||
EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId, true);
|
||||
if (response != null) {
|
||||
bridgeHandler.sendMessage(response.getERP1Message(), null);
|
||||
logger.debug("Teach in response for {} with new senderId {} (= offset {}) sent", enoceanId,
|
||||
HexUtils.bytesToHex(newSenderId), offset);
|
||||
} else {
|
||||
logger.warn("Teach in response for enoceanId {} not supported!", enoceanId);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Could not get new SenderIdOffset");
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private void sendTeachOutResponse(ERP1Message msg, String enoceanId, Thing thing) {
|
||||
byte[] senderId = bridgeHandler.getBaseId();
|
||||
senderId[3] += (byte) thing.getConfiguration().getProperties().getOrDefault(PARAMETER_SENDERIDOFFSET, 0);
|
||||
|
||||
// send response
|
||||
EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, senderId, false);
|
||||
if (response != null) {
|
||||
bridgeHandler.sendMessage(response.getERP1Message(), null);
|
||||
logger.debug("Teach out response for thing {} with EnOceanId {} sent", thing.getUID().getId(), enoceanId);
|
||||
} else {
|
||||
logger.warn("Teach out response for enoceanId {} not supported!", enoceanId);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createDiscoveryResult(EEP eep, boolean broadcastMessages, Integer senderIdOffset) {
|
||||
String enoceanId = HexUtils.bytesToHex(eep.getSenderId());
|
||||
ThingTypeUID thingTypeUID = eep.getThingTypeUID();
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeHandler.getThing().getUID(), enoceanId);
|
||||
|
||||
int senderIdOffset = 0;
|
||||
boolean broadcastMessages = true;
|
||||
|
||||
// check for bidirectional communication => do not use broadcast in this case
|
||||
if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0]
|
||||
& UTEResponse.CommunicationType_MASK) == UTEResponse.CommunicationType_MASK) {
|
||||
broadcastMessages = false;
|
||||
}
|
||||
|
||||
// if ute => send response if needed
|
||||
if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] & UTEResponse.ResponseNeeded_MASK) == 0) {
|
||||
logger.info("Sending UTE response to {}", enoceanId);
|
||||
senderIdOffset = sendTeachInResponse(msg, enoceanId);
|
||||
}
|
||||
|
||||
// if 4BS teach in variation 3 => send response
|
||||
if ((eep instanceof _4BSMessage) && ((_4BSMessage) eep).isTeachInVariation3Supported()) {
|
||||
logger.info("Sending 4BS teach in variation 3 response to {}", enoceanId);
|
||||
senderIdOffset = sendTeachInResponse(msg, enoceanId);
|
||||
}
|
||||
|
||||
DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID)
|
||||
.withRepresentationProperty(enoceanId).withBridge(bridgeHandler.getThing().getUID());
|
||||
.withRepresentationProperty(PARAMETER_ENOCEANID).withProperty(PARAMETER_ENOCEANID, enoceanId)
|
||||
.withProperty(PARAMETER_BROADCASTMESSAGES, broadcastMessages)
|
||||
.withBridge(bridgeHandler.getThing().getUID());
|
||||
|
||||
eep.addConfigPropertiesTo(discoveryResultBuilder);
|
||||
discoveryResultBuilder.withProperty(PARAMETER_BROADCASTMESSAGES, broadcastMessages);
|
||||
discoveryResultBuilder.withProperty(PARAMETER_ENOCEANID, enoceanId);
|
||||
|
||||
if (senderIdOffset > 0) {
|
||||
if (senderIdOffset != null) {
|
||||
// advance config with new device id
|
||||
discoveryResultBuilder.withProperty(PARAMETER_SENDERIDOFFSET, senderIdOffset);
|
||||
}
|
||||
|
||||
thingDiscovered(discoveryResultBuilder.build());
|
||||
|
||||
// As we only support sensors to be teached in, we do not need to send a teach in response => 4bs
|
||||
// bidirectional teach in proc is not supported yet
|
||||
// this is true except for UTE teach in => we always have to send a response here
|
||||
}
|
||||
|
||||
private int sendTeachInResponse(ERP1Message msg, String enoceanId) {
|
||||
int offset;
|
||||
// get new sender Id
|
||||
offset = bridgeHandler.getNextSenderId(enoceanId);
|
||||
if (offset > 0) {
|
||||
byte[] newSenderId = bridgeHandler.getBaseId();
|
||||
newSenderId[3] += offset;
|
||||
|
||||
// send response
|
||||
EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId);
|
||||
if (response != null) {
|
||||
bridgeHandler.sendMessage(response.getERP1Message(), null);
|
||||
logger.info("Teach in response for {} with new senderId {} (= offset {}) sent", enoceanId,
|
||||
HexUtils.bytesToHex(newSenderId), offset);
|
||||
} else {
|
||||
logger.warn("Teach in response for enoceanId {} not supported!", enoceanId);
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSenderIdToListenTo() {
|
||||
public long getEnOceanIdToListenTo() {
|
||||
// we just want teach in msg, so return zero here
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -51,9 +50,6 @@ public abstract class A5_02 extends _4BSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
double scaledTemp = getScaledMin()
|
||||
- (((getUnscaledMin() - getUnscaledTemperatureValue()) * (getScaledMin() - getScaledMax()))
|
||||
|
||||
@ -62,9 +62,6 @@ public abstract class A5_04 extends _4BSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
if (channelId.equals(CHANNEL_TEMPERATURE)) {
|
||||
double scaledTemp = getScaledTemperatureMin()
|
||||
|
||||
@ -53,9 +53,6 @@ public abstract class A5_07 extends _4BSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
if (channelId.equals(CHANNEL_ILLUMINATION)) {
|
||||
return getIllumination();
|
||||
|
||||
@ -71,9 +71,6 @@ public abstract class A5_08 extends _4BSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
if (channelId.equals(CHANNEL_TEMPERATURE)) {
|
||||
double scaledTemp = getScaledTemperatureMin()
|
||||
|
||||
@ -40,9 +40,6 @@ public abstract class A5_10 extends _4BSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
switch (channelId) {
|
||||
case CHANNEL_FANSPEEDSTAGE:
|
||||
|
||||
@ -169,7 +169,7 @@ public class A5_20_04 extends A5_20 {
|
||||
@Override
|
||||
protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (CHANNEL_SEND_COMMAND.equals(channelId) && (command.equals(OnOffType.ON))) {
|
||||
if (VIRTUALCHANNEL_SEND_COMMAND.equals(channelId)) {
|
||||
byte db3 = getPos(getCurrentStateFunc);
|
||||
byte db2 = getTsp(getCurrentStateFunc);
|
||||
byte db1 = (byte) (0x00 | getMc(getCurrentStateFunc) | getWuc(getCurrentStateFunc));
|
||||
|
||||
@ -55,9 +55,6 @@ public class PTM200Message extends _RPSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
switch (channelId) {
|
||||
case CHANNEL_GENERAL_SWITCHING:
|
||||
@ -77,4 +74,9 @@ public class PTM200Message extends _RPSMessage {
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,11 +27,12 @@ public class UTEResponse extends _VLDMessage {
|
||||
public static final byte ResponseNeeded_MASK = 0x40;
|
||||
public static final byte TeachIn_NotSpecified = 0x20;
|
||||
|
||||
public UTEResponse(ERP1Message packet) {
|
||||
public UTEResponse(ERP1Message packet, boolean teachIn) {
|
||||
int dataLength = packet.getPayload().length - ESP3_SENDERID_LENGTH - ESP3_RORG_LENGTH - ESP3_STATUS_LENGTH;
|
||||
|
||||
setData(packet.getPayload(ESP3_RORG_LENGTH, dataLength));
|
||||
bytes[0] = (byte) 0x91; // bidirectional communication, teach in accepted, teach in response
|
||||
bytes[0] = (byte) (teachIn ? 0x91 : 0xA1); // bidirectional communication, teach in accepted or teach out, teach
|
||||
// in response
|
||||
|
||||
setStatus((byte) 0x80);
|
||||
setSuppressRepeating(true);
|
||||
|
||||
@ -23,11 +23,11 @@ import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
|
||||
*/
|
||||
public class _4BSTeachInVariation3Response extends _4BSMessage {
|
||||
|
||||
public _4BSTeachInVariation3Response(ERP1Message packet) {
|
||||
public _4BSTeachInVariation3Response(ERP1Message packet, boolean teachIn) {
|
||||
byte[] payload = packet.getPayload(ESP3_RORG_LENGTH, RORG._4BS.getDataLength());
|
||||
|
||||
payload[3] = (byte) 0xF0; // telegram with EEP number and Manufacturer ID,
|
||||
// EEP supported, Sender ID stored, Response
|
||||
payload[3] = (byte) (teachIn ? 0xF0 : 0xD0); // telegram with EEP number and Manufacturer ID,
|
||||
// EEP supported, Sender ID stored or deleted, Response
|
||||
|
||||
setData(payload);
|
||||
setDestinationId(packet.getSenderId());
|
||||
|
||||
@ -51,4 +51,6 @@ public abstract class _RPSMessage extends EEP {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract boolean isValidForTeachIn();
|
||||
}
|
||||
|
||||
@ -98,7 +98,8 @@ public class D2_05_00 extends _VLDMessage {
|
||||
|
||||
@Override
|
||||
public void addConfigPropertiesTo(DiscoveryResultBuilder discoveredThingResultBuilder) {
|
||||
discoveredThingResultBuilder.withProperty(PARAMETER_EEPID, getEEPType().getId());
|
||||
discoveredThingResultBuilder.withProperty(PARAMETER_SENDINGEEPID, getEEPType().getId())
|
||||
.withProperty(PARAMETER_RECEIVINGEEPID, getEEPType().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -15,18 +15,24 @@ package org.openhab.binding.enocean.internal.eep;
|
||||
import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.enocean.internal.eep.Base.UTEResponse;
|
||||
import org.openhab.binding.enocean.internal.eep.Base._4BSMessage;
|
||||
import org.openhab.binding.enocean.internal.eep.Base._4BSTeachInVariation3Response;
|
||||
import org.openhab.binding.enocean.internal.eep.Base._RPSMessage;
|
||||
import org.openhab.binding.enocean.internal.eep.D5_00.D5_00_01;
|
||||
import org.openhab.binding.enocean.internal.eep.F6_01.F6_01_01;
|
||||
import org.openhab.binding.enocean.internal.eep.F6_02.F6_02_01;
|
||||
import org.openhab.binding.enocean.internal.eep.F6_05.F6_05_02;
|
||||
import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00;
|
||||
import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_00_EltakoFPE;
|
||||
import org.openhab.binding.enocean.internal.eep.F6_10.F6_10_01;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage;
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.SMACKTeachInResponse;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -45,8 +51,9 @@ public class EEPFactory {
|
||||
if (cl == null) {
|
||||
throw new IllegalArgumentException("Message " + eepType + " not implemented");
|
||||
}
|
||||
return cl.newInstance();
|
||||
} catch (IllegalAccessException | InstantiationException e) {
|
||||
return cl.getDeclaredConstructor().newInstance();
|
||||
} catch (IllegalAccessException | InstantiationException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
@ -69,6 +76,21 @@ public class EEPFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private static EEPType getGenericEEPTypeFor(byte rorg) {
|
||||
logger.info("Received unsupported EEP teach in, trying to fallback to generic thing");
|
||||
RORG r = RORG.getRORG(rorg);
|
||||
if (r == RORG._4BS) {
|
||||
logger.info("Fallback to 4BS generic thing");
|
||||
return EEPType.Generic4BS;
|
||||
} else if (r == RORG.VLD) {
|
||||
logger.info("Fallback to VLD generic thing");
|
||||
return EEPType.GenericVLD;
|
||||
} else {
|
||||
logger.info("Fallback not possible");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static EEP buildEEPFromTeachInERP1(ERP1Message msg) {
|
||||
if (!msg.getIsTeachIn() && !(msg.getRORG() == RORG.RPS)) {
|
||||
return null;
|
||||
@ -77,38 +99,48 @@ public class EEPFactory {
|
||||
switch (msg.getRORG()) {
|
||||
case RPS:
|
||||
try {
|
||||
EEP result = new F6_01_01(msg);
|
||||
if (result.isValid()) { // check if t21 is set, nu not set, and data == 0x10 or 0x00
|
||||
_RPSMessage result = new F6_10_00(msg);
|
||||
if (result.isValidForTeachIn()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
EEP result = new F6_02_01(msg);
|
||||
if (result.isValid()) { // check if highest bit is not set
|
||||
_RPSMessage result = new F6_10_01(msg);
|
||||
if (result.isValidForTeachIn()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
EEP result = new F6_10_00(msg);
|
||||
if (result.isValid()) {
|
||||
_RPSMessage result = new F6_02_01(msg);
|
||||
if (result.isValidForTeachIn()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
EEP result = new F6_10_00_EltakoFPE(msg);
|
||||
if (result.isValid()) { // check if data == 0x10 or 0x00
|
||||
_RPSMessage result = new F6_05_02(msg);
|
||||
if (result.isValidForTeachIn()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
EEP result = new F6_10_01(msg);
|
||||
if (result.isValid()) {
|
||||
_RPSMessage result = new F6_01_01(msg);
|
||||
if (result.isValidForTeachIn()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
_RPSMessage result = new F6_10_00_EltakoFPE(msg);
|
||||
if (result.isValidForTeachIn()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -120,8 +152,8 @@ public class EEPFactory {
|
||||
case _4BS: {
|
||||
int db_0 = msg.getPayload()[4];
|
||||
if ((db_0 & _4BSMessage.LRN_Type_Mask) == 0) { // Variation 1
|
||||
logger.info("Received 4BS Teach In variation 1 without EEP");
|
||||
return null;
|
||||
logger.info("Received 4BS Teach In variation 1 without EEP, fallback to generic thing");
|
||||
return buildEEP(EEPType.Generic4BS, msg);
|
||||
}
|
||||
|
||||
byte db_3 = msg.getPayload()[1];
|
||||
@ -132,19 +164,21 @@ public class EEPFactory {
|
||||
int type = ((db_3 & 0b11) << 5) + ((db_2 & 0xFF) >>> 3);
|
||||
int manufId = ((db_2 & 0b111) << 8) + (db_1 & 0xff);
|
||||
|
||||
logger.info("Received 4BS Teach In with EEP A5-{}-{} and manufacturerID {}",
|
||||
logger.debug("Received 4BS Teach In with EEP A5-{}-{} and manufacturerID {}",
|
||||
HexUtils.bytesToHex(new byte[] { (byte) func }),
|
||||
HexUtils.bytesToHex(new byte[] { (byte) type }),
|
||||
HexUtils.bytesToHex(new byte[] { (byte) manufId }));
|
||||
|
||||
EEPType eepType = EEPType.getType(RORG._4BS, func, type, manufId);
|
||||
if (eepType == null) {
|
||||
logger.debug("Received unsupported EEP teach in, fallback to generic thing");
|
||||
eepType = EEPType.Generic4BS;
|
||||
eepType = getGenericEEPTypeFor(RORG._4BS.getValue());
|
||||
}
|
||||
|
||||
return buildEEP(eepType, msg);
|
||||
if (eepType != null) {
|
||||
return buildEEP(eepType, msg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UTE: {
|
||||
byte[] payload = msg.getPayload();
|
||||
|
||||
@ -161,38 +195,58 @@ public class EEPFactory {
|
||||
|
||||
EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
|
||||
if (eepType == null) {
|
||||
logger.info("Received unsupported EEP teach in, fallback to generic thing");
|
||||
RORG r = RORG.getRORG(rorg);
|
||||
if (r == RORG._4BS) {
|
||||
eepType = EEPType.Generic4BS;
|
||||
} else if (r == RORG.VLD) {
|
||||
eepType = EEPType.GenericVLD;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
eepType = getGenericEEPTypeFor(rorg);
|
||||
}
|
||||
|
||||
return buildEEP(eepType, msg);
|
||||
if (eepType != null) {
|
||||
return buildEEP(eepType, msg);
|
||||
}
|
||||
}
|
||||
case Unknown:
|
||||
case VLD:
|
||||
case MSC:
|
||||
case SIG:
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId) {
|
||||
public static EEP buildEEPFromTeachInSMACKEvent(EventMessage event) {
|
||||
if (event.getEventMessageType() != EventMessageType.SA_CONFIRM_LEARN) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] payload = event.getPayload();
|
||||
byte manufIdMSB = payload[2];
|
||||
byte manufIdLSB = payload[3];
|
||||
int manufId = ((manufIdMSB & 0b111) << 8) + (manufIdLSB & 0xff);
|
||||
|
||||
byte rorg = payload[4];
|
||||
int func = payload[5] & 0xFF;
|
||||
int type = payload[6] & 0xFF;
|
||||
|
||||
byte[] senderId = Arrays.copyOfRange(payload, 12, 12 + 4);
|
||||
|
||||
logger.debug("Received SMACK Teach In with EEP {}-{}-{} and manufacturerID {}",
|
||||
HexUtils.bytesToHex(new byte[] { (byte) rorg }), HexUtils.bytesToHex(new byte[] { (byte) func }),
|
||||
HexUtils.bytesToHex(new byte[] { (byte) type }), HexUtils.bytesToHex(new byte[] { (byte) manufId }));
|
||||
|
||||
EEPType eepType = EEPType.getType(RORG.getRORG(rorg), func, type, manufId);
|
||||
if (eepType == null) {
|
||||
eepType = getGenericEEPTypeFor(rorg);
|
||||
}
|
||||
|
||||
return createEEP(eepType).setSenderId(senderId);
|
||||
}
|
||||
|
||||
public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId, boolean teachIn) {
|
||||
switch (msg.getRORG()) {
|
||||
case UTE:
|
||||
EEP result = new UTEResponse(msg);
|
||||
EEP result = new UTEResponse(msg, teachIn);
|
||||
result.setSenderId(senderId);
|
||||
|
||||
return result;
|
||||
case _4BS:
|
||||
result = new _4BSTeachInVariation3Response(msg);
|
||||
result = new _4BSTeachInVariation3Response(msg, teachIn);
|
||||
result.setSenderId(senderId);
|
||||
|
||||
return result;
|
||||
@ -200,4 +254,31 @@ public class EEPFactory {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SMACKTeachInResponse buildResponseFromSMACKTeachIn(EventMessage event, boolean sendTeachOuts) {
|
||||
SMACKTeachInResponse response = new SMACKTeachInResponse();
|
||||
|
||||
byte priority = event.getPayload()[1];
|
||||
if ((priority & 0b1001) == 0b1001) {
|
||||
logger.debug("gtw is already postmaster");
|
||||
if (sendTeachOuts) {
|
||||
logger.debug("Repeated learn is not allow hence send teach out");
|
||||
response.setTeachOutResponse();
|
||||
} else {
|
||||
logger.debug("Send a repeated learn in");
|
||||
response.setRepeatedTeachInResponse();
|
||||
}
|
||||
} else if ((priority & 0b100) == 0) {
|
||||
logger.debug("no place for further mailbox");
|
||||
response.setNoPlaceForFurtherMailbox();
|
||||
} else if ((priority & 0b10) == 0) {
|
||||
logger.debug("rssi is not good enough");
|
||||
response.setBadRSSI();
|
||||
} else if ((priority & 0b1) == 0b1) {
|
||||
logger.debug("gtw is candidate for postmaster => teach in");
|
||||
response.setTeachIn();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.openhab.binding.enocean.internal.EnOceanChannelDescription;
|
||||
import org.openhab.binding.enocean.internal.config.EnOceanChannelTransformationConfig;
|
||||
import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_01;
|
||||
import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_02;
|
||||
import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_03;
|
||||
@ -164,15 +165,9 @@ public enum EEPType {
|
||||
UTEResponse(RORG.UTE, 0, 0, false, UTEResponse.class, null),
|
||||
_4BSTeachInVariation3Response(RORG._4BS, 0, 0, false, _4BSTeachInVariation3Response.class, null),
|
||||
|
||||
GenericRPS(RORG.RPS, 0xFF, 0xFF, false, GenericRPS.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH,
|
||||
CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING,
|
||||
CHANNEL_GENERIC_COLOR, CHANNEL_GENERIC_TEACHINCMD),
|
||||
Generic4BS(RORG._4BS, 0xFF, 0xFF, false, Generic4BS.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH,
|
||||
CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING,
|
||||
CHANNEL_GENERIC_COLOR, CHANNEL_GENERIC_TEACHINCMD, CHANNEL_VIBRATION),
|
||||
GenericVLD(RORG.VLD, 0xFF, 0xFF, false, GenericVLD.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH,
|
||||
CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING,
|
||||
CHANNEL_GENERIC_COLOR, CHANNEL_GENERIC_TEACHINCMD),
|
||||
GenericRPS(RORG.RPS, 0xFF, 0xFF, false, GenericRPS.class, THING_TYPE_GENERICTHING),
|
||||
Generic4BS(RORG._4BS, 0xFF, 0xFF, false, Generic4BS.class, THING_TYPE_GENERICTHING, CHANNEL_VIBRATION),
|
||||
GenericVLD(RORG.VLD, 0xFF, 0xFF, false, GenericVLD.class, THING_TYPE_GENERICTHING),
|
||||
|
||||
PTM200(RORG.RPS, 0x00, 0x00, false, PTM200Message.class, null, CHANNEL_GENERAL_SWITCHING, CHANNEL_ROLLERSHUTTER,
|
||||
CHANNEL_CONTACT),
|
||||
@ -391,8 +386,8 @@ public enum EEPType {
|
||||
|
||||
// UniversalCommand(RORG._4BS, 0x3f, 0x7f, false, A5_3F_7F_Universal.class, THING_TYPE_UNIVERSALACTUATOR,
|
||||
// CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_LIGHT_SWITCHING, CHANNEL_GENERIC_DIMMER, CHANNEL_TEACHINCMD),
|
||||
EltakoFSB(RORG._4BS, 0x3f, 0x7f, false, "EltakoFSB", 0, A5_3F_7F_EltakoFSB.class, THING_TYPE_ROLLERSHUTTER, 0,
|
||||
new Hashtable<String, Configuration>() {
|
||||
EltakoFSB(RORG._4BS, 0x3f, 0x7f, false, false, "EltakoFSB", 0, A5_3F_7F_EltakoFSB.class, THING_TYPE_ROLLERSHUTTER,
|
||||
0, new Hashtable<String, Configuration>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
{
|
||||
put(CHANNEL_ROLLERSHUTTER, new Configuration());
|
||||
@ -404,10 +399,10 @@ public enum EEPType {
|
||||
}
|
||||
}),
|
||||
|
||||
Thermostat(RORG._4BS, 0x20, 0x04, false, A5_20_04.class, THING_TYPE_THERMOSTAT, CHANNEL_VALVE_POSITION,
|
||||
Thermostat(RORG._4BS, 0x20, 0x04, false, true, A5_20_04.class, THING_TYPE_THERMOSTAT, CHANNEL_VALVE_POSITION,
|
||||
CHANNEL_BUTTON_LOCK, CHANNEL_DISPLAY_ORIENTATION, CHANNEL_TEMPERATURE_SETPOINT, CHANNEL_TEMPERATURE,
|
||||
CHANNEL_FEED_TEMPERATURE, CHANNEL_MEASUREMENT_CONTROL, CHANNEL_FAILURE_CODE, CHANNEL_WAKEUPCYCLE,
|
||||
CHANNEL_SERVICECOMMAND, CHANNEL_STATUS_REQUEST_EVENT, CHANNEL_SEND_COMMAND),
|
||||
CHANNEL_SERVICECOMMAND),
|
||||
|
||||
SwitchWithEnergyMeasurment_00(RORG.VLD, 0x01, 0x00, true, D2_01_00.class, THING_TYPE_MEASUREMENTSWITCH,
|
||||
CHANNEL_GENERAL_SWITCHING, CHANNEL_TOTALUSAGE),
|
||||
@ -512,23 +507,36 @@ public enum EEPType {
|
||||
|
||||
private boolean supportsRefresh;
|
||||
|
||||
private boolean requestsResponse;
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, Class<? extends EEP> eepClass,
|
||||
ThingTypeUID thingTypeUID, String... channelIds) {
|
||||
this(rorg, func, type, supportsRefresh, eepClass, thingTypeUID, -1, channelIds);
|
||||
}
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse,
|
||||
Class<? extends EEP> eepClass, ThingTypeUID thingTypeUID, String... channelIds) {
|
||||
this(rorg, func, type, supportsRefresh, requestsResponse, eepClass, thingTypeUID, -1, channelIds);
|
||||
}
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, String manufactorSuffix, int manufId,
|
||||
Class<? extends EEP> eepClass, ThingTypeUID thingTypeUID, String... channelIds) {
|
||||
this(rorg, func, type, supportsRefresh, manufactorSuffix, manufId, eepClass, thingTypeUID, 0, channelIds);
|
||||
this(rorg, func, type, supportsRefresh, false, manufactorSuffix, manufId, eepClass, thingTypeUID, 0,
|
||||
channelIds);
|
||||
}
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, Class<? extends EEP> eepClass,
|
||||
ThingTypeUID thingTypeUID, int command, String... channelIds) {
|
||||
this(rorg, func, type, supportsRefresh, "", 0, eepClass, thingTypeUID, command, channelIds);
|
||||
this(rorg, func, type, supportsRefresh, false, "", 0, eepClass, thingTypeUID, command, channelIds);
|
||||
}
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, String manufactorSuffix, int manufId,
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse,
|
||||
Class<? extends EEP> eepClass, ThingTypeUID thingTypeUID, int command, String... channelIds) {
|
||||
this(rorg, func, type, supportsRefresh, requestsResponse, "", 0, eepClass, thingTypeUID, command, channelIds);
|
||||
}
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse, String manufactorSuffix,
|
||||
int manufId, Class<? extends EEP> eepClass, ThingTypeUID thingTypeUID, int command, String... channelIds) {
|
||||
this.rorg = rorg;
|
||||
this.func = func;
|
||||
this.type = type;
|
||||
@ -538,24 +546,18 @@ public enum EEPType {
|
||||
this.manufactorSuffix = manufactorSuffix;
|
||||
this.manufactorId = manufId;
|
||||
this.supportsRefresh = supportsRefresh;
|
||||
this.requestsResponse = requestsResponse;
|
||||
|
||||
for (String id : channelIds) {
|
||||
this.channelIdsWithConfig.put(id, new Configuration());
|
||||
this.supportedChannels.put(id, CHANNELID2CHANNELDESCRIPTION.get(id));
|
||||
}
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_RSSI, new Configuration());
|
||||
this.supportedChannels.put(CHANNEL_RSSI, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_RSSI));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_REPEATCOUNT, new Configuration());
|
||||
this.supportedChannels.put(CHANNEL_REPEATCOUNT, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_REPEATCOUNT));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_LASTRECEIVED, new Configuration());
|
||||
this.supportedChannels.put(CHANNEL_LASTRECEIVED, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_LASTRECEIVED));
|
||||
addDefaultChannels();
|
||||
}
|
||||
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, String manufactorSuffix, int manufId,
|
||||
Class<? extends EEP> eepClass, ThingTypeUID thingTypeUID, int command,
|
||||
EEPType(RORG rorg, int func, int type, boolean supportsRefresh, boolean requestsResponse, String manufactorSuffix,
|
||||
int manufId, Class<? extends EEP> eepClass, ThingTypeUID thingTypeUID, int command,
|
||||
Hashtable<String, Configuration> channelConfigs) {
|
||||
this.rorg = rorg;
|
||||
this.func = func;
|
||||
@ -567,11 +569,46 @@ public enum EEPType {
|
||||
this.manufactorSuffix = manufactorSuffix;
|
||||
this.manufactorId = manufId;
|
||||
this.supportsRefresh = supportsRefresh;
|
||||
this.requestsResponse = requestsResponse;
|
||||
|
||||
for (String id : channelConfigs.keySet()) {
|
||||
this.supportedChannels.put(id, CHANNELID2CHANNELDESCRIPTION.get(id));
|
||||
}
|
||||
|
||||
addDefaultChannels();
|
||||
}
|
||||
|
||||
private void addDefaultChannels() {
|
||||
|
||||
if (THING_TYPE_GENERICTHING.equals(this.thingTypeUID)) {
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_SWITCH, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_SWITCH,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_SWITCH));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_ROLLERSHUTTER, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_ROLLERSHUTTER,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_ROLLERSHUTTER));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_DIMMER, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_DIMMER,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_DIMMER));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_NUMBER, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_NUMBER,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_NUMBER));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_STRING, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_STRING,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_STRING));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_COLOR, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_COLOR, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_COLOR));
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_GENERIC_TEACHINCMD, new EnOceanChannelTransformationConfig());
|
||||
this.supportedChannels.put(CHANNEL_GENERIC_TEACHINCMD,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_GENERIC_TEACHINCMD));
|
||||
}
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_RSSI, new Configuration());
|
||||
this.supportedChannels.put(CHANNEL_RSSI, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_RSSI));
|
||||
|
||||
@ -580,6 +617,12 @@ public enum EEPType {
|
||||
|
||||
this.channelIdsWithConfig.put(CHANNEL_LASTRECEIVED, new Configuration());
|
||||
this.supportedChannels.put(CHANNEL_LASTRECEIVED, CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_LASTRECEIVED));
|
||||
|
||||
if (requestsResponse) {
|
||||
this.channelIdsWithConfig.put(CHANNEL_STATUS_REQUEST_EVENT, new Configuration());
|
||||
this.supportedChannels.put(CHANNEL_STATUS_REQUEST_EVENT,
|
||||
CHANNELID2CHANNELDESCRIPTION.get(CHANNEL_STATUS_REQUEST_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
public Class<? extends EEP> getEEPClass() {
|
||||
@ -602,6 +645,10 @@ public enum EEPType {
|
||||
return supportsRefresh;
|
||||
}
|
||||
|
||||
public boolean getRequstesResponse() {
|
||||
return requestsResponse;
|
||||
}
|
||||
|
||||
public Map<String, EnOceanChannelDescription> GetSupportedChannels() {
|
||||
return Collections.unmodifiableMap(supportedChannels);
|
||||
}
|
||||
@ -614,7 +661,7 @@ public enum EEPType {
|
||||
}
|
||||
|
||||
public boolean isChannelSupported(String channelId, String channelTypeId) {
|
||||
return supportedChannels.containsKey(channelId)
|
||||
return supportedChannels.containsKey(channelId) || VIRTUALCHANNEL_SEND_COMMAND.equals(channelId)
|
||||
|| supportedChannels.values().stream().anyMatch(c -> c.channelTypeUID.getId().equals(channelTypeId));
|
||||
}
|
||||
|
||||
|
||||
@ -34,9 +34,6 @@ public class F6_01_01 extends _RPSMessage {
|
||||
@Override
|
||||
protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent,
|
||||
Configuration config) {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getBit(bytes[0], 4) ? CommonTriggerEvents.PRESSED : CommonTriggerEvents.RELEASED;
|
||||
}
|
||||
@ -45,4 +42,10 @@ public class F6_01_01 extends _RPSMessage {
|
||||
protected boolean validateData(byte[] bytes) {
|
||||
return super.validateData(bytes) && !getBit(bytes[0], 7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
// just treat press as teach in, ignore release
|
||||
return t21 && !nu && bytes[0] == 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,9 +55,6 @@ public class F6_02_01 extends _RPSMessage {
|
||||
@Override
|
||||
protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent,
|
||||
Configuration config) {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (t21 && nu) {
|
||||
byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? A0 : B0;
|
||||
@ -112,11 +109,6 @@ public class F6_02_01 extends _RPSMessage {
|
||||
// this method is used by the classic device listener channels to convert an rocker switch message into an
|
||||
// appropriate item update
|
||||
State currentState = getCurrentStateFunc.apply(channelId);
|
||||
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
if (t21 && nu) {
|
||||
EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class);
|
||||
byte dir1 = c.getChannel() == Channel.ChannelA ? A0 : B0;
|
||||
@ -179,4 +171,22 @@ public class F6_02_01 extends _RPSMessage {
|
||||
protected boolean validateData(byte[] bytes) {
|
||||
return super.validateData(bytes) && !getBit(bytes[0], 7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
if (t21) {
|
||||
// just treat press as teach in => DB0.4 has to be set
|
||||
if (!getBit(bytes[0], 4)) {
|
||||
return false;
|
||||
}
|
||||
// DB0.7 is never set for rocker switch message
|
||||
if (getBit(bytes[0], 7)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,9 +52,6 @@ public class F6_02_02 extends _RPSMessage {
|
||||
@Override
|
||||
protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent,
|
||||
Configuration config) {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (t21 && nu) {
|
||||
byte dir1 = channelId.equals(CHANNEL_ROCKERSWITCH_CHANNELA) ? AI : BI;
|
||||
@ -109,11 +106,6 @@ public class F6_02_02 extends _RPSMessage {
|
||||
// this method is used by the classic device listener channels to convert an rocker switch message into an
|
||||
// appropriate item update
|
||||
State currentState = getCurrentStateFunc.apply(channelId);
|
||||
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
if (t21 && nu) {
|
||||
EnOceanChannelVirtualRockerSwitchConfig c = config.as(EnOceanChannelVirtualRockerSwitchConfig.class);
|
||||
byte dir1 = c.getChannel() == Channel.ChannelA ? AI : BI;
|
||||
@ -171,4 +163,9 @@ public class F6_02_02 extends _RPSMessage {
|
||||
private State inverse(UpDownType currentState) {
|
||||
return currentState == UpDownType.UP ? UpDownType.DOWN : UpDownType.UP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
return false; // Never treat a message as F6-02-02, let user decide which orientation of rocker switch is used
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,9 +44,6 @@ public class F6_05_02 extends _RPSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
switch (channelId) {
|
||||
case CHANNEL_SMOKEDETECTION:
|
||||
@ -62,4 +59,10 @@ public class F6_05_02 extends _RPSMessage {
|
||||
protected boolean validateData(byte[] bytes) {
|
||||
return super.validateData(bytes) && (bytes[0] == ALARM_OFF || bytes[0] == ALARM_ON || bytes[0] == ENERGY_LOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
// just treat the first message with ALARM_ON as teach in
|
||||
return !t21 && !nu && bytes[0] == ALARM_ON;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,9 +47,6 @@ public class F6_10_00 extends _RPSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
byte data = (byte) (bytes[0] & 0xF0);
|
||||
|
||||
@ -82,4 +79,9 @@ public class F6_10_00 extends _RPSMessage {
|
||||
protected boolean validateData(byte[] bytes) {
|
||||
return super.validateData(bytes) && getBit(bytes[0], 7) && getBit(bytes[0], 6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
return t21 && !nu && getBit(bytes[0], 7) && getBit(bytes[0], 6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,4 +61,10 @@ public class F6_10_00_EltakoFPE extends _RPSMessage {
|
||||
// FPE just sends 0b00010000 or 0b00000000 value, so we apply mask 0b11101111
|
||||
return super.validateData(bytes) && ((bytes[0] & (byte) 0xEF) == (byte) 0x00);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
// just treat CLOSED as teach in
|
||||
return bytes[0] == CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,9 +47,6 @@ public class F6_10_01 extends _RPSMessage {
|
||||
@Override
|
||||
protected State convertToStateImpl(String channelId, String channelTypeId,
|
||||
Function<String, State> getCurrentStateFunc, Configuration config) {
|
||||
if (!isValid()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
byte data = (byte) (bytes[0] & 0x0F);
|
||||
|
||||
@ -82,4 +79,10 @@ public class F6_10_01 extends _RPSMessage {
|
||||
protected boolean validateData(byte[] bytes) {
|
||||
return super.validateData(bytes) && getBit(bytes[0], 6) && getBit(bytes[0], 3) && getBit(bytes[0], 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidForTeachIn() {
|
||||
return !getBit(bytes[0], 7) && getBit(bytes[0], 6) && !getBit(bytes[0], 5) && !getBit(bytes[0], 4)
|
||||
&& getBit(bytes[0], 3) && getBit(bytes[0], 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.eep.Generic;
|
||||
|
||||
import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.PARAMETER_EEPID;
|
||||
import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
|
||||
import static org.openhab.binding.enocean.internal.messages.ESP3Packet.*;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -161,6 +161,7 @@ public class GenericEEP extends EEP {
|
||||
|
||||
@Override
|
||||
public void addConfigPropertiesTo(DiscoveryResultBuilder discoveredThingResultBuilder) {
|
||||
discoveredThingResultBuilder.withProperty(PARAMETER_EEPID, getEEPType().getId());
|
||||
discoveredThingResultBuilder.withProperty(PARAMETER_SENDINGEEPID, getEEPType().getId())
|
||||
.withProperty(PARAMETER_RECEIVINGEEPID, getEEPType().getId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import org.openhab.binding.enocean.internal.eep.EEPFactory;
|
||||
import org.openhab.binding.enocean.internal.eep.EEPType;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
@ -68,8 +69,8 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler {
|
||||
* @param senderIdOffset to be validated
|
||||
* @return true if senderIdOffset is between ]0;128[ and is not used yet
|
||||
*/
|
||||
private boolean validateSenderIdOffset(int senderIdOffset) {
|
||||
if (senderIdOffset == -1) {
|
||||
private boolean validateSenderIdOffset(Integer senderIdOffset) {
|
||||
if (senderIdOffset == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -157,26 +158,24 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler {
|
||||
}
|
||||
|
||||
private boolean initializeIdForSending() {
|
||||
// Generic things are treated as actuator things, however to support also generic sensors one can define a
|
||||
// senderIdOffset of -1
|
||||
// TODO: seperate generic actuators from generic sensors?
|
||||
String thingTypeId = this.getThing().getThingTypeUID().getId();
|
||||
String genericThingTypeId = THING_TYPE_GENERICTHING.getId();
|
||||
|
||||
if (getConfiguration().senderIdOffset == -1 && thingTypeId.equals(genericThingTypeId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
EnOceanBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if senderIdOffset is not set (=> defaults to -1) or set to -1, the next free senderIdOffset is determined
|
||||
if (getConfiguration().senderIdOffset == -1) {
|
||||
// Generic things are treated as actuator things, however to support also generic sensors one can omit
|
||||
// senderIdOffset
|
||||
// TODO: seperate generic actuators from generic sensors?
|
||||
if ((getConfiguration().senderIdOffset == null
|
||||
&& THING_TYPE_GENERICTHING.equals(this.getThing().getThingTypeUID()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if senderIdOffset is not set, the next free senderIdOffset is determined
|
||||
if (getConfiguration().senderIdOffset == null) {
|
||||
Configuration updateConfig = editConfiguration();
|
||||
getConfiguration().senderIdOffset = bridgeHandler.getNextSenderId(thing);
|
||||
if (getConfiguration().senderIdOffset == -1) {
|
||||
if (getConfiguration().senderIdOffset == null) {
|
||||
configurationErrorDescription = "Could not get a free sender Id from Bridge";
|
||||
return false;
|
||||
}
|
||||
@ -185,12 +184,10 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler {
|
||||
}
|
||||
|
||||
byte[] baseId = bridgeHandler.getBaseId();
|
||||
baseId[3] = (byte) ((baseId[3] & 0xFF) + getConfiguration().senderIdOffset);
|
||||
baseId[3] = (byte) ((baseId[3] + getConfiguration().senderIdOffset) & 0xFF);
|
||||
this.senderId = baseId;
|
||||
|
||||
this.updateProperty(PROPERTY_ENOCEAN_ID, HexUtils.bytesToHex(this.senderId));
|
||||
this.updateProperty(PROPERTY_SENDINGENOCEAN_ID, HexUtils.bytesToHex(this.senderId));
|
||||
bridgeHandler.addSender(getConfiguration().senderIdOffset, thing);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -203,6 +200,22 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequestResponse() {
|
||||
sendMessage(VIRTUALCHANNEL_SEND_COMMAND, VIRTUALCHANNEL_SEND_COMMAND, OnOffType.ON, null);
|
||||
}
|
||||
|
||||
protected void sendMessage(String channelId, String channelTypeId, Command command, Configuration channelConfig) {
|
||||
EEP eep = EEPFactory.createEEP(sendingEEPType);
|
||||
if (eep.convertFromCommand(channelId, channelTypeId, command, id -> getCurrentState(id), channelConfig)
|
||||
.hasData()) {
|
||||
BasePacket msg = eep.setSenderId(senderId).setDestinationId(destinationId)
|
||||
.setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message();
|
||||
|
||||
getBridgeHandler().sendMessage(msg, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// We must have a valid sendingEEPType and sender id to send commands
|
||||
@ -237,16 +250,7 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler {
|
||||
|
||||
try {
|
||||
Configuration channelConfig = channel.getConfiguration();
|
||||
|
||||
EEP eep = EEPFactory.createEEP(sendingEEPType);
|
||||
if (eep.convertFromCommand(channelId, channelTypeId, command, id -> getCurrentState(id), channelConfig)
|
||||
.hasData()) {
|
||||
BasePacket msg = eep.setSenderId(senderId).setDestinationId(destinationId)
|
||||
.setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message();
|
||||
|
||||
getBridgeHandler().sendMessage(msg, null);
|
||||
}
|
||||
|
||||
sendMessage(channelId, channelTypeId, command, channelConfig);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Exception while sending telegram!", e);
|
||||
}
|
||||
@ -254,11 +258,16 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler {
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
if (getConfiguration().senderIdOffset > 0) {
|
||||
EnOceanBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
|
||||
EnOceanBridgeHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
if (getConfiguration().senderIdOffset != null && getConfiguration().senderIdOffset > 0) {
|
||||
bridgeHandler.removeSender(getConfiguration().senderIdOffset);
|
||||
}
|
||||
|
||||
if (bridgeHandler.isSmackClient(this.thing)) {
|
||||
logger.warn("Removing smack client (ThingId: {}) without teach out!", this.thing.getUID().getId());
|
||||
}
|
||||
}
|
||||
|
||||
super.handleRemoval();
|
||||
|
||||
@ -19,8 +19,11 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig;
|
||||
import org.openhab.binding.enocean.internal.eep.EEP;
|
||||
import org.openhab.binding.enocean.internal.eep.EEPFactory;
|
||||
@ -57,6 +60,8 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements
|
||||
|
||||
protected final Hashtable<RORG, EEPType> receivingEEPTypes = new Hashtable<>();
|
||||
|
||||
protected ScheduledFuture<?> responseFuture = null;
|
||||
|
||||
public EnOceanBaseSensorHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) {
|
||||
super(thing, itemChannelLinkRegistry);
|
||||
}
|
||||
@ -104,7 +109,7 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSenderIdToListenTo() {
|
||||
public long getEnOceanIdToListenTo() {
|
||||
return Long.parseLong(config.enoceanId, 16);
|
||||
}
|
||||
|
||||
@ -129,6 +134,10 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements
|
||||
};
|
||||
}
|
||||
|
||||
protected void sendRequestResponse() {
|
||||
throw new NotImplementedException("Sensor cannot send responses");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(BasePacket packet) {
|
||||
ERP1Message msg = (ERP1Message) packet;
|
||||
@ -175,6 +184,15 @@ public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (receivingEEPType.getRequstesResponse()) {
|
||||
// fire trigger for receive
|
||||
triggerChannel(prepareAnswer, "requestAnswer");
|
||||
// Send response after 100ms
|
||||
if (responseFuture == null || responseFuture.isDone()) {
|
||||
responseFuture = scheduler.schedule(this::sendRequestResponse, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.handler;
|
||||
|
||||
import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Collection;
|
||||
import java.util.Hashtable;
|
||||
@ -63,18 +65,25 @@ public abstract class EnOceanBaseThingHandler extends ConfigStatusThingHandler {
|
||||
|
||||
private ItemChannelLinkRegistry itemChannelLinkRegistry;
|
||||
|
||||
protected @NonNull ChannelUID prepareAnswer;
|
||||
|
||||
public EnOceanBaseThingHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) {
|
||||
super(thing);
|
||||
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
|
||||
prepareAnswer = new ChannelUID(thing.getUID(), CHANNEL_STATUS_REQUEST_EVENT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing enocean base thing handler.");
|
||||
this.gateway = null; // reset gateway in case we change the bridge
|
||||
this.config = null;
|
||||
initializeThing((getBridge() == null) ? null : getBridge().getStatus());
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "A bridge is required");
|
||||
} else {
|
||||
initializeThing(bridge.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeThing(ThingStatus bridgeStatus) {
|
||||
@ -143,6 +152,10 @@ public abstract class EnOceanBaseThingHandler extends ConfigStatusThingHandler {
|
||||
String channelId = entry.getKey();
|
||||
EnOceanChannelDescription cd = entry.getValue().GetSupportedChannels().get(channelId);
|
||||
|
||||
if (cd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we do not need to auto create channel => skip
|
||||
if (!cd.autoCreate) {
|
||||
return;
|
||||
|
||||
@ -15,30 +15,35 @@ package org.openhab.binding.enocean.internal.handler;
|
||||
import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.openhab.binding.enocean.internal.EnOceanConfigStatusMessage;
|
||||
import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig;
|
||||
import org.openhab.binding.enocean.internal.config.EnOceanBridgeConfig;
|
||||
import org.openhab.binding.enocean.internal.config.EnOceanBridgeConfig.ESPVersion;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket;
|
||||
import org.openhab.binding.enocean.internal.messages.BaseResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory;
|
||||
import org.openhab.binding.enocean.internal.messages.RDBaseIdResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.RDRepeaterResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.RDVersionResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
import org.openhab.binding.enocean.internal.messages.Response.ResponseType;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.BaseResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.RDBaseIdResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.RDLearnedClientsResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.RDLearnedClientsResponse.LearnedClient;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.RDRepeaterResponse;
|
||||
import org.openhab.binding.enocean.internal.messages.Responses.RDVersionResponse;
|
||||
import org.openhab.binding.enocean.internal.transceiver.EnOceanESP2Transceiver;
|
||||
import org.openhab.binding.enocean.internal.transceiver.EnOceanESP3Transceiver;
|
||||
import org.openhab.binding.enocean.internal.transceiver.EnOceanTransceiver;
|
||||
import org.openhab.binding.enocean.internal.transceiver.PacketListener;
|
||||
import org.openhab.binding.enocean.internal.transceiver.ResponseListener;
|
||||
import org.openhab.binding.enocean.internal.transceiver.ResponseListenerIgnoringTimeouts;
|
||||
import org.openhab.binding.enocean.internal.transceiver.TeachInListener;
|
||||
import org.openhab.binding.enocean.internal.transceiver.TransceiverErrorListener;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||
@ -76,9 +81,12 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
private byte[] baseId = null;
|
||||
private Thing[] sendingThings = new Thing[128];
|
||||
|
||||
private int nextSenderId = 0;
|
||||
private SerialPortManager serialPortManager;
|
||||
|
||||
private boolean smackAvailable = false;
|
||||
private boolean sendTeachOuts = true;
|
||||
private Set<String> smackClients = Set.of();
|
||||
|
||||
public EnOceanBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) {
|
||||
super(bridge);
|
||||
this.serialPortManager = serialPortManager;
|
||||
@ -157,13 +165,6 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"SerialPortManager could not be found");
|
||||
} else {
|
||||
Object devId = getConfig().get(NEXTSENDERID);
|
||||
if (devId != null) {
|
||||
nextSenderId = ((BigDecimal) devId).intValue();
|
||||
} else {
|
||||
nextSenderId = 0;
|
||||
}
|
||||
|
||||
if (connectorTask == null || connectorTask.isDone()) {
|
||||
connectorTask = scheduler.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override
|
||||
@ -187,9 +188,12 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
switch (c.getESPVersion()) {
|
||||
case ESP2:
|
||||
transceiver = new EnOceanESP2Transceiver(c.path, this, scheduler, serialPortManager);
|
||||
smackAvailable = false;
|
||||
sendTeachOuts = false;
|
||||
break;
|
||||
case ESP3:
|
||||
transceiver = new EnOceanESP3Transceiver(c.path, this, scheduler, serialPortManager);
|
||||
sendTeachOuts = c.sendTeachOuts;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -200,6 +204,7 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "starting rx thread...");
|
||||
transceiver.StartReceiving(scheduler);
|
||||
logger.info("EnOceanSerialTransceiver RX thread up and running");
|
||||
|
||||
if (c.rs485) {
|
||||
if (c.rs485BaseId != null && !c.rs485BaseId.isEmpty()) {
|
||||
@ -238,6 +243,28 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (c.getESPVersion() == ESPVersion.ESP3) {
|
||||
logger.debug("set postmaster mailboxes");
|
||||
transceiver.sendBasePacket(ESP3PacketFactory.SA_WR_POSTMASTER((byte) (c.enableSmack ? 20 : 0)),
|
||||
new ResponseListenerIgnoringTimeouts<BaseResponse>() {
|
||||
|
||||
@Override
|
||||
public void responseReceived(BaseResponse response) {
|
||||
|
||||
logger.debug("received response for postmaster mailboxes");
|
||||
if (response.isOK()) {
|
||||
updateProperty("Postmaster mailboxes:",
|
||||
Integer.toString(c.enableSmack ? 20 : 0));
|
||||
smackAvailable = c.enableSmack;
|
||||
refreshProperties();
|
||||
} else {
|
||||
updateProperty("Postmaster mailboxes:", "Not supported");
|
||||
smackAvailable = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("request version info");
|
||||
@ -283,7 +310,7 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
Collection<ConfigStatusMessage> configStatusMessages = new LinkedList<>();
|
||||
|
||||
// The serial port must be provided
|
||||
String path = (String) getThing().getConfiguration().get(PATH);
|
||||
String path = getThing().getConfiguration().as(EnOceanBridgeConfig.class).path;
|
||||
if (path == null || path.isEmpty()) {
|
||||
configStatusMessages.add(ConfigStatusMessage.Builder.error(PATH)
|
||||
.withMessageKeySuffix(EnOceanConfigStatusMessage.PORT_MISSING.getMessageKey()).withArguments(PATH)
|
||||
@ -297,30 +324,33 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
return baseId.clone();
|
||||
}
|
||||
|
||||
public int getNextSenderId(Thing sender) {
|
||||
// TODO: change id to enoceanId
|
||||
public boolean isSmackClient(Thing sender) {
|
||||
return smackClients.contains(sender.getConfiguration().as(EnOceanBaseConfig.class).enoceanId);
|
||||
}
|
||||
|
||||
public Integer getNextSenderId(Thing sender) {
|
||||
return getNextSenderId(sender.getConfiguration().as(EnOceanBaseConfig.class).enoceanId);
|
||||
}
|
||||
|
||||
public int getNextSenderId(String senderId) {
|
||||
if (nextSenderId != 0 && sendingThings[nextSenderId] == null) {
|
||||
int result = nextSenderId;
|
||||
Configuration config = getConfig();
|
||||
config.put(NEXTSENDERID, null);
|
||||
updateConfiguration(config);
|
||||
nextSenderId = 0;
|
||||
public Integer getNextSenderId(String enoceanId) {
|
||||
EnOceanBridgeConfig config = getConfigAs(EnOceanBridgeConfig.class);
|
||||
|
||||
return result;
|
||||
if (config.nextSenderId != null && sendingThings[config.nextSenderId] == null) {
|
||||
Configuration c = this.editConfiguration();
|
||||
c.put(PARAMETER_NEXT_SENDERID, null);
|
||||
updateConfiguration(c);
|
||||
|
||||
return config.nextSenderId;
|
||||
}
|
||||
|
||||
for (byte i = 1; i < sendingThings.length; i++) {
|
||||
for (int i = 1; i < sendingThings.length; i++) {
|
||||
if (sendingThings[i] == null || sendingThings[i].getConfiguration().as(EnOceanBaseConfig.class).enoceanId
|
||||
.equalsIgnoreCase(senderId)) {
|
||||
.equalsIgnoreCase(enoceanId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean existsSender(int id, Thing sender) {
|
||||
@ -345,7 +375,7 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
}
|
||||
|
||||
public void addPacketListener(PacketListener listener) {
|
||||
addPacketListener(listener, listener.getSenderIdToListenTo());
|
||||
addPacketListener(listener, listener.getEnOceanIdToListenTo());
|
||||
}
|
||||
|
||||
public void addPacketListener(PacketListener listener, long senderIdToListenTo) {
|
||||
@ -355,7 +385,7 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
}
|
||||
|
||||
public void removePacketListener(PacketListener listener) {
|
||||
removePacketListener(listener, listener.getSenderIdToListenTo());
|
||||
removePacketListener(listener, listener.getEnOceanIdToListenTo());
|
||||
}
|
||||
|
||||
public void removePacketListener(PacketListener listener, long senderIdToListenTo) {
|
||||
@ -364,12 +394,74 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
}
|
||||
}
|
||||
|
||||
public void startDiscovery(PacketListener teachInListener) {
|
||||
public void startDiscovery(TeachInListener teachInListener) {
|
||||
transceiver.startDiscovery(teachInListener);
|
||||
|
||||
if (smackAvailable) {
|
||||
// activate smack teach in
|
||||
logger.debug("activate smack teach in");
|
||||
try {
|
||||
transceiver.sendBasePacket(ESP3PacketFactory.SA_WR_LEARNMODE(true),
|
||||
new ResponseListenerIgnoringTimeouts<BaseResponse>() {
|
||||
|
||||
@Override
|
||||
public void responseReceived(BaseResponse response) {
|
||||
|
||||
if (response.isOK()) {
|
||||
logger.debug("Smack teach in activated");
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Smack packet could not be send: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopDiscovery() {
|
||||
transceiver.stopDiscovery();
|
||||
|
||||
try {
|
||||
transceiver.sendBasePacket(ESP3PacketFactory.SA_WR_LEARNMODE(false), null);
|
||||
refreshProperties();
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Smack packet could not be send: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshProperties() {
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE && smackAvailable) {
|
||||
|
||||
logger.debug("request learned smack clients");
|
||||
try {
|
||||
transceiver.sendBasePacket(ESP3PacketFactory.SA_RD_LEARNEDCLIENTS,
|
||||
new ResponseListenerIgnoringTimeouts<RDLearnedClientsResponse>() {
|
||||
|
||||
@Override
|
||||
public void responseReceived(RDLearnedClientsResponse response) {
|
||||
|
||||
logger.debug("received response for learned smack clients");
|
||||
if (response.isValid() && response.isOK()) {
|
||||
LearnedClient[] clients = response.getLearnedClients();
|
||||
updateProperty("Learned smart ack clients", Integer.toString(clients.length));
|
||||
updateProperty("Smart ack clients",
|
||||
Arrays.stream(clients)
|
||||
.map(x -> String.format("%s (MB Idx: %d)",
|
||||
HexUtils.bytesToHex(x.clientId), x.mailboxIndex))
|
||||
.collect(Collectors.joining(", ")));
|
||||
smackClients = Arrays.stream(clients).map(x -> HexUtils.bytesToHex(x.clientId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Smack packet could not be send: " + e.getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -378,4 +470,8 @@ public class EnOceanBridgeHandler extends ConfigStatusBridgeHandler implements T
|
||||
transceiver = null;
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, exception.getMessage());
|
||||
}
|
||||
|
||||
public boolean sendTeachOuts() {
|
||||
return sendTeachOuts;
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ public class EnOceanClassicDeviceHandler extends EnOceanBaseActuatorHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSenderIdToListenTo() {
|
||||
public long getEnOceanIdToListenTo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -13,8 +13,10 @@
|
||||
package org.openhab.binding.enocean.internal.messages;
|
||||
|
||||
import org.openhab.binding.enocean.internal.EnOceanBindingConstants;
|
||||
import org.openhab.binding.enocean.internal.Helper;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType;
|
||||
import org.openhab.binding.enocean.internal.messages.CCMessage.CCMessageType;
|
||||
import org.openhab.binding.enocean.internal.messages.SAMessage.SAMessageType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
|
||||
/**
|
||||
@ -42,6 +44,28 @@ public class ESP3PacketFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public static BasePacket SA_WR_LEARNMODE(boolean activate) {
|
||||
return new SAMessage(SAMessageType.SA_WR_LEARNMODE,
|
||||
new byte[] { SAMessageType.SA_WR_LEARNMODE.getValue(), (byte) (activate ? 1 : 0), 0, 0, 0, 0, 0 });
|
||||
}
|
||||
|
||||
public final static BasePacket SA_RD_LEARNEDCLIENTS = new SAMessage(SAMessageType.SA_RD_LEARNEDCLIENTS);
|
||||
|
||||
public static BasePacket SA_RD_MAILBOX_STATUS(byte[] clientId, byte[] controllerId) {
|
||||
return new SAMessage(SAMessageType.SA_RD_MAILBOX_STATUS,
|
||||
Helper.concatAll(new byte[] { SAMessageType.SA_RD_MAILBOX_STATUS.getValue() }, clientId, controllerId));
|
||||
}
|
||||
|
||||
public static BasePacket SA_WR_POSTMASTER(byte mailboxes) {
|
||||
return new SAMessage(SAMessageType.SA_WR_POSTMASTER,
|
||||
new byte[] { SAMessageType.SA_WR_POSTMASTER.getValue(), mailboxes });
|
||||
}
|
||||
|
||||
public static BasePacket SA_WR_CLIENTLEARNRQ(byte manu1, byte manu2, byte rorg, byte func, byte type) {
|
||||
return new SAMessage(SAMessageType.SA_WR_CLIENTLEARNRQ,
|
||||
new byte[] { SAMessageType.SA_WR_CLIENTLEARNRQ.getValue(), manu1, manu2, rorg, func, type });
|
||||
}
|
||||
|
||||
public static BasePacket BuildPacket(int dataLength, int optionalDataLength, byte packetType, byte[] payload) {
|
||||
ESPPacketType type = ESPPacketType.getPacketType(packetType);
|
||||
|
||||
@ -50,6 +74,8 @@ public class ESP3PacketFactory {
|
||||
return new Response(dataLength, optionalDataLength, payload);
|
||||
case RADIO_ERP1:
|
||||
return new ERP1Message(dataLength, optionalDataLength, payload);
|
||||
case EVENT:
|
||||
return new EventMessage(dataLength, optionalDataLength, payload);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.enocean.internal.messages;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public class EventMessage extends BasePacket {
|
||||
|
||||
public enum EventMessageType {
|
||||
UNKNOWN((byte) 0x00, 1),
|
||||
SA_RECLAIM_NOT_SUCCESSFUL((byte) 0x01, 1),
|
||||
SA_CONFIRM_LEARN((byte) 0x02, 17),
|
||||
SA_LEARN_ACK((byte) 0x03, 4),
|
||||
CO_READY((byte) 0x04, 2),
|
||||
CO_EVENT_SECUREDEVICES((byte) 0x05, 6),
|
||||
CO_DUTYCYCLE_LIMIT((byte) 0x06, 2),
|
||||
CO_TRANSMIT_FAILED((byte) 0x07, 2);
|
||||
|
||||
private byte value;
|
||||
private int dataLength;
|
||||
|
||||
EventMessageType(byte value, int dataLength) {
|
||||
this.value = value;
|
||||
this.dataLength = dataLength;
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public int getDataLength() {
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
public static EventMessageType getEventMessageType(byte value) {
|
||||
return Stream.of(EventMessageType.values()).filter(t -> t.value == value).findFirst().orElse(UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
private EventMessageType type;
|
||||
|
||||
EventMessage(int dataLength, int optionalDataLength, byte[] payload) {
|
||||
super(dataLength, optionalDataLength, ESPPacketType.EVENT, payload);
|
||||
|
||||
type = EventMessageType.getEventMessageType(payload[0]);
|
||||
}
|
||||
|
||||
public EventMessageType getEventMessageType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ public class Response extends BasePacket {
|
||||
protected ResponseType responseType;
|
||||
protected boolean _isValid = false;
|
||||
|
||||
protected Response(int dataLength, int optionalDataLength, byte[] payload) {
|
||||
public Response(int dataLength, int optionalDataLength, byte[] payload) {
|
||||
super(dataLength, optionalDataLength, ESPPacketType.RESPONSE, payload);
|
||||
|
||||
try {
|
||||
|
||||
@ -10,9 +10,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.messages;
|
||||
package org.openhab.binding.enocean.internal.messages.Responses;
|
||||
|
||||
import org.openhab.binding.enocean.internal.Helper;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -10,9 +10,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.messages;
|
||||
package org.openhab.binding.enocean.internal.messages.Responses;
|
||||
|
||||
import org.openhab.binding.enocean.internal.Helper;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.enocean.internal.messages.Responses;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.enocean.internal.Helper;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public class RDLearnedClientsResponse extends Response {
|
||||
|
||||
public class LearnedClient {
|
||||
public byte[] clientId;
|
||||
public byte[] controllerId;
|
||||
public int mailboxIndex;
|
||||
}
|
||||
|
||||
LearnedClient[] learnedClients;
|
||||
|
||||
public RDLearnedClientsResponse(Response response) {
|
||||
this(response.getPayload().length, response.getOptionalPayload().length,
|
||||
Helper.concatAll(response.getPayload(), response.getOptionalPayload()));
|
||||
}
|
||||
|
||||
RDLearnedClientsResponse(int dataLength, int optionalDataLength, byte[] payload) {
|
||||
super(dataLength, optionalDataLength, payload);
|
||||
|
||||
if (payload == null || ((payload.length - 1) % 9) != 0) {
|
||||
return;
|
||||
} else {
|
||||
_isValid = true;
|
||||
}
|
||||
|
||||
learnedClients = new LearnedClient[(payload.length - 1) / 9];
|
||||
for (int i = 0; i < learnedClients.length; i++) {
|
||||
LearnedClient client = new LearnedClient();
|
||||
client.clientId = Arrays.copyOfRange(payload, 1 + i * 9, 1 + i * 9 + 4);
|
||||
client.controllerId = Arrays.copyOfRange(payload, 5 + i * 9, 5 + i * 9 + 4);
|
||||
client.mailboxIndex = payload[9 + i * 9] & 0xFF;
|
||||
learnedClients[i] = client;
|
||||
}
|
||||
}
|
||||
|
||||
public LearnedClient[] getLearnedClients() {
|
||||
return learnedClients;
|
||||
}
|
||||
}
|
||||
@ -10,11 +10,12 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.messages;
|
||||
package org.openhab.binding.enocean.internal.messages.Responses;
|
||||
|
||||
import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
|
||||
/**
|
||||
@ -10,11 +10,12 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.enocean.internal.messages;
|
||||
package org.openhab.binding.enocean.internal.messages.Responses;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
|
||||
/**
|
||||
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.enocean.internal.messages.Responses;
|
||||
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public class SMACKTeachInResponse extends Response {
|
||||
|
||||
// set response time to 250ms
|
||||
static final byte RESPONSE_TIME_HVALUE = 0;
|
||||
static final byte RESPONSE_TIME_LVALUE = (byte) 0xFA;
|
||||
|
||||
static final byte TEACH_IN = 0x00;
|
||||
static final byte TEACH_OUT = 0x20;
|
||||
static final byte REPEATED_TEACH_IN = 0x01;
|
||||
static final byte NOPLACE_FOR_MAILBOX = 0x12;
|
||||
static final byte BAD_RSSI = 0x14;
|
||||
|
||||
public SMACKTeachInResponse() {
|
||||
super(4, 0, new byte[] { Response.ResponseType.RET_OK.getValue(), RESPONSE_TIME_HVALUE, RESPONSE_TIME_LVALUE,
|
||||
TEACH_IN });
|
||||
}
|
||||
|
||||
public void setTeachOutResponse() {
|
||||
data[3] = TEACH_OUT;
|
||||
}
|
||||
|
||||
public boolean isTeachOut() {
|
||||
return data[3] == TEACH_OUT;
|
||||
}
|
||||
|
||||
public void setRepeatedTeachInResponse() {
|
||||
data[3] = REPEATED_TEACH_IN;
|
||||
}
|
||||
|
||||
public void setNoPlaceForFurtherMailbox() {
|
||||
data[3] = NOPLACE_FOR_MAILBOX;
|
||||
}
|
||||
|
||||
public void setBadRSSI() {
|
||||
data[3] = BAD_RSSI;
|
||||
}
|
||||
|
||||
public void setTeachIn() {
|
||||
data[3] = TEACH_IN;
|
||||
}
|
||||
|
||||
public boolean isTeachIn() {
|
||||
return data[3] == TEACH_IN;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.enocean.internal.messages;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public class SAMessage extends BasePacket {
|
||||
|
||||
public enum SAMessageType {
|
||||
SA_WR_LEARNMODE((byte) 0x01, 7),
|
||||
SA_RD_LEARNMODE((byte) 0x02, 1),
|
||||
SA_WR_LEARNCONFIRM((byte) 0x03, 1),
|
||||
SA_WR_CLIENTLEARNRQ((byte) 0x04, 6),
|
||||
SA_WR_RESET((byte) 0x05, 1),
|
||||
SA_RD_LEARNEDCLIENTS((byte) 0x06, 1),
|
||||
SA_WR_RECLAIMS((byte) 0x07, 1),
|
||||
SA_WR_POSTMASTER((byte) 0x08, 2),
|
||||
SA_RD_MAILBOX_STATUS((byte) 0x09, 9);
|
||||
|
||||
private byte value;
|
||||
private int dataLength;
|
||||
|
||||
SAMessageType(byte value, int dataLength) {
|
||||
this.value = value;
|
||||
this.dataLength = dataLength;
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public int getDataLength() {
|
||||
return dataLength;
|
||||
}
|
||||
}
|
||||
|
||||
private SAMessageType type;
|
||||
|
||||
public SAMessage(SAMessageType type) {
|
||||
this(type, new byte[] { type.getValue() });
|
||||
}
|
||||
|
||||
public SAMessage(SAMessageType type, byte[] payload) {
|
||||
super(type.getDataLength(), 0, ESPPacketType.SMART_ACK_COMMAND, payload);
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public SAMessageType getSAMessageType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@ -18,8 +18,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.openhab.binding.enocean.internal.EnOceanException;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
|
||||
import org.openhab.binding.enocean.internal.messages.ESP3Packet;
|
||||
import org.openhab.binding.enocean.internal.messages.ESP3PacketFactory;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
@ -138,21 +136,8 @@ public class EnOceanESP3Transceiver extends EnOceanTransceiver {
|
||||
HexUtils.bytesToHex(packet.getPayload()));
|
||||
break;
|
||||
case EVENT:
|
||||
logger.debug("Event occured: {}", HexUtils.bytesToHex(packet.getPayload()));
|
||||
break;
|
||||
case RADIO_ERP1: {
|
||||
ERP1Message msg = (ERP1Message) packet;
|
||||
logger.debug("{} with RORG {} for {} payload {} received",
|
||||
packet.getPacketType().name(), msg.getRORG().name(),
|
||||
HexUtils.bytesToHex(msg.getSenderId()), HexUtils.bytesToHex(
|
||||
Arrays.copyOf(dataBuffer, dataLength + optionalLength)));
|
||||
|
||||
if (msg.getRORG() != RORG.Unknown) {
|
||||
informListeners(msg);
|
||||
} else {
|
||||
logger.debug("Received unknown RORG");
|
||||
}
|
||||
}
|
||||
case RADIO_ERP1:
|
||||
informListeners(packet);
|
||||
break;
|
||||
case RADIO_ERP2:
|
||||
break;
|
||||
|
||||
@ -27,9 +27,13 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.enocean.internal.EnOceanBindingConstants;
|
||||
import org.openhab.binding.enocean.internal.EnOceanException;
|
||||
import org.openhab.binding.enocean.internal.Helper;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket;
|
||||
import org.openhab.binding.enocean.internal.messages.BasePacket.ESPPacketType;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message;
|
||||
import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG;
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage;
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage.EventMessageType;
|
||||
import org.openhab.binding.enocean.internal.messages.Response;
|
||||
import org.openhab.core.io.transport.serial.PortInUseException;
|
||||
import org.openhab.core.io.transport.serial.SerialPort;
|
||||
@ -138,7 +142,8 @@ public abstract class EnOceanTransceiver implements SerialPortEventListener {
|
||||
Request currentRequest = null;
|
||||
|
||||
protected Map<Long, HashSet<PacketListener>> listeners;
|
||||
protected PacketListener teachInListener;
|
||||
protected HashSet<EventListener> eventListeners;
|
||||
protected TeachInListener teachInListener;
|
||||
|
||||
protected InputStream inputStream;
|
||||
protected OutputStream outputStream;
|
||||
@ -151,6 +156,7 @@ public abstract class EnOceanTransceiver implements SerialPortEventListener {
|
||||
requestQueue = new RequestQueue(scheduler);
|
||||
|
||||
listeners = new HashMap<>();
|
||||
eventListeners = new HashSet<>();
|
||||
teachInListener = null;
|
||||
|
||||
this.errorListener = errorListener;
|
||||
@ -192,6 +198,7 @@ public abstract class EnOceanTransceiver implements SerialPortEventListener {
|
||||
}
|
||||
});
|
||||
}
|
||||
logger.info("EnOceanSerialTransceiver RX thread started");
|
||||
}
|
||||
|
||||
public void ShutDown() {
|
||||
@ -266,36 +273,65 @@ public abstract class EnOceanTransceiver implements SerialPortEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
protected void informListeners(ERP1Message msg) {
|
||||
protected void informListeners(BasePacket packet) {
|
||||
try {
|
||||
byte[] senderId = msg.getSenderId();
|
||||
if (packet.getPacketType() == ESPPacketType.RADIO_ERP1) {
|
||||
ERP1Message msg = (ERP1Message) packet;
|
||||
byte[] senderId = msg.getSenderId();
|
||||
byte[] d = Helper.concatAll(msg.getPayload(), msg.getOptionalPayload());
|
||||
|
||||
if (senderId != null) {
|
||||
if (filteredDeviceId != null && senderId[0] == filteredDeviceId[0] && senderId[1] == filteredDeviceId[1]
|
||||
&& senderId[2] == filteredDeviceId[2]) {
|
||||
// filter away own messages which are received through a repeater
|
||||
return;
|
||||
}
|
||||
logger.debug("{} with RORG {} for {} payload {} received", packet.getPacketType().name(),
|
||||
msg.getRORG().name(), HexUtils.bytesToHex(msg.getSenderId()), HexUtils.bytesToHex(d));
|
||||
|
||||
if (teachInListener != null) {
|
||||
if (msg.getIsTeachIn() || (msg.getRORG() == RORG.RPS)) {
|
||||
logger.info("Received teach in message from {}", HexUtils.bytesToHex(msg.getSenderId()));
|
||||
teachInListener.packetReceived(msg);
|
||||
return;
|
||||
if (msg.getRORG() != RORG.Unknown) {
|
||||
if (senderId != null) {
|
||||
if (filteredDeviceId != null && senderId[0] == filteredDeviceId[0]
|
||||
&& senderId[1] == filteredDeviceId[1] && senderId[2] == filteredDeviceId[2]) {
|
||||
// filter away own messages which are received through a repeater
|
||||
return;
|
||||
}
|
||||
|
||||
if (teachInListener != null && (msg.getIsTeachIn() || msg.getRORG() == RORG.RPS)) {
|
||||
logger.info("Received teach in message from {}", HexUtils.bytesToHex(msg.getSenderId()));
|
||||
teachInListener.packetReceived(msg);
|
||||
return;
|
||||
} else if (teachInListener == null && msg.getIsTeachIn()) {
|
||||
logger.info("Discard message because this is a teach-in telegram from {}!",
|
||||
HexUtils.bytesToHex(msg.getSenderId()));
|
||||
return;
|
||||
}
|
||||
|
||||
long s = Long.parseLong(HexUtils.bytesToHex(senderId), 16);
|
||||
HashSet<PacketListener> pl = listeners.get(s);
|
||||
if (pl != null) {
|
||||
pl.forEach(l -> l.packetReceived(msg));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (msg.getIsTeachIn()) {
|
||||
logger.info("Discard message because this is a teach-in telegram from {}!",
|
||||
HexUtils.bytesToHex(msg.getSenderId()));
|
||||
logger.debug("Received unknown RORG");
|
||||
}
|
||||
} else if (packet.getPacketType() == ESPPacketType.EVENT) {
|
||||
EventMessage event = (EventMessage) packet;
|
||||
|
||||
byte[] d = Helper.concatAll(packet.getPayload(), packet.getOptionalPayload());
|
||||
logger.debug("{} with type {} payload {} received", ESPPacketType.EVENT.name(),
|
||||
event.getEventMessageType().name(), HexUtils.bytesToHex(d));
|
||||
|
||||
if (event.getEventMessageType() == EventMessageType.SA_CONFIRM_LEARN) {
|
||||
byte[] senderId = event.getPayload(EventMessageType.SA_CONFIRM_LEARN.getDataLength() - 5, 4);
|
||||
|
||||
if (teachInListener != null) {
|
||||
logger.info("Received smart teach in from {}", HexUtils.bytesToHex(senderId));
|
||||
teachInListener.eventReceived(event);
|
||||
return;
|
||||
} else {
|
||||
logger.info("Discard message because this is a smart teach-in telegram from {}!",
|
||||
HexUtils.bytesToHex(senderId));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long s = Long.parseLong(HexUtils.bytesToHex(senderId), 16);
|
||||
HashSet<PacketListener> pl = listeners.get(s);
|
||||
if (pl != null) {
|
||||
pl.forEach(l -> l.packetReceived(msg));
|
||||
}
|
||||
eventListeners.forEach(l -> l.eventReceived(event));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception in informListeners", e);
|
||||
@ -354,7 +390,15 @@ public abstract class EnOceanTransceiver implements SerialPortEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void startDiscovery(PacketListener teachInListener) {
|
||||
public void addEventMessageListener(EventListener listener) {
|
||||
eventListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeEventMessageListener(EventListener listener) {
|
||||
eventListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void startDiscovery(TeachInListener teachInListener) {
|
||||
this.teachInListener = teachInListener;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.enocean.internal.transceiver;
|
||||
|
||||
import org.openhab.binding.enocean.internal.messages.EventMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public interface EventListener {
|
||||
public void eventReceived(EventMessage event);
|
||||
}
|
||||
@ -22,5 +22,5 @@ public interface PacketListener {
|
||||
|
||||
public void packetReceived(BasePacket packet);
|
||||
|
||||
public long getSenderIdToListenTo();
|
||||
public long getEnOceanIdToListenTo();
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.enocean.internal.transceiver;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Daniel Weber - Initial contribution
|
||||
*/
|
||||
public interface TeachInListener extends PacketListener, EventListener {
|
||||
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
<description>EnOceanId of device this thing belongs to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<description>EnOceanId of device this thing belongs to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<description>EnOceanId of device this thing belongs to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<description>EnOceanId of device this thing belongs to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<description>EnOceanId of device this thing belongs to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<description>EnOceanId of device this thing belongs to</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="senderIdOffset" type="integer">
|
||||
<parameter name="senderIdOffset" type="integer" required="false" min="1" max="127">
|
||||
<label>Sender Id</label>
|
||||
<description>Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be
|
||||
determined by bridge</description>
|
||||
|
||||
@ -31,6 +31,11 @@
|
||||
<required>true</required>
|
||||
<default>ESP3</default>
|
||||
</parameter>
|
||||
<parameter name="enableSmack" type="boolean">
|
||||
<label>Handle SMACK Messages</label>
|
||||
<description>Declare Gateway as a SMACK Postmaster and handle SMACK messages</description>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="rs485" type="boolean">
|
||||
<advanced>true</advanced>
|
||||
<label>Gateway Connected Directly to RS485 BUS</label>
|
||||
@ -41,9 +46,15 @@
|
||||
<label>Use following BaseId when connected directly to RS485 BUS</label>
|
||||
<default>00000000</default>
|
||||
</parameter>
|
||||
<parameter name="nextSenderId" type="integer">
|
||||
<parameter name="sendTeachOuts" type="boolean">
|
||||
<advanced>true</advanced>
|
||||
<label>Next Device Id</label>
|
||||
<label>Send TeachOuts</label>
|
||||
<description>Should a learned in or teach out response been send on a repeated smack teach in request</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="nextSenderId" type="integer" min="1" max="127">
|
||||
<advanced>true</advanced>
|
||||
<label>Next Sender Id Offset</label>
|
||||
<description>Defines the next device Id, if empty, the next device id is automatically determined</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
@ -488,13 +488,6 @@
|
||||
<event/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sendCommand">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Send Command</label>
|
||||
<description>You can send telegrams to the device by switching this channel on</description>
|
||||
<category>thermostat</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="smokeDetection">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Smoke Detected</label>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user