[lcn] Fix several bugs in measurement processing with firmware before 2013 (#9991)
Signed-off-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
parent
d5e83e395b
commit
3a2c996aab
@ -12,6 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.lcn.internal;
|
package org.openhab.binding.lcn.internal;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
@ -38,6 +40,8 @@ public class LcnBindingConstants {
|
|||||||
public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group");
|
public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group");
|
||||||
/** Regex for address in PCK protocol */
|
/** Regex for address in PCK protocol */
|
||||||
public static final String ADDRESS_REGEX = "[:=%]M(?<segId>\\d{3})(?<modId>\\d{3})";
|
public static final String ADDRESS_REGEX = "[:=%]M(?<segId>\\d{3})(?<modId>\\d{3})";
|
||||||
|
public static final Pattern MEASUREMENT_PATTERN_BEFORE_2013 = Pattern
|
||||||
|
.compile(LcnBindingConstants.ADDRESS_REGEX + "\\.(?<value>\\d{5})");
|
||||||
/** LCN coding for ACK */
|
/** LCN coding for ACK */
|
||||||
public static final int CODE_ACK = -1;
|
public static final int CODE_ACK = -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,6 +68,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class LcnModuleHandler extends BaseThingHandler {
|
public class LcnModuleHandler extends BaseThingHandler {
|
||||||
private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class);
|
||||||
|
private static final int FIRMWARE_VERSION_LENGTH = 6;
|
||||||
private static final Map<String, Converter> VALUE_CONVERTERS = new HashMap<>();
|
private static final Map<String, Converter> VALUE_CONVERTERS = new HashMap<>();
|
||||||
private static final InversionConverter INVERSION_CONVERTER = new InversionConverter();
|
private static final InversionConverter INVERSION_CONVERTER = new InversionConverter();
|
||||||
private @Nullable LcnAddrMod moduleAddress;
|
private @Nullable LcnAddrMod moduleAddress;
|
||||||
@ -95,11 +96,11 @@ public class LcnModuleHandler extends BaseThingHandler {
|
|||||||
LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId);
|
LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Determine serial number of manually added modules
|
ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress);
|
||||||
|
readFirmwareVersionFromProperty().ifPresent(info::setFirmwareVersion);
|
||||||
requestFirmwareVersionAndSerialNumberIfNotSet();
|
requestFirmwareVersionAndSerialNumberIfNotSet();
|
||||||
|
|
||||||
// create sub handlers
|
// create sub handlers
|
||||||
ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress);
|
|
||||||
for (LcnChannelGroup type : LcnChannelGroup.values()) {
|
for (LcnChannelGroup type : LcnChannelGroup.values()) {
|
||||||
subHandlers.put(type, type.createSubHandler(this, info));
|
subHandlers.put(type, type.createSubHandler(this, info));
|
||||||
}
|
}
|
||||||
@ -158,8 +159,7 @@ public class LcnModuleHandler extends BaseThingHandler {
|
|||||||
* @throws LcnException when the handler is not initialized
|
* @throws LcnException when the handler is not initialized
|
||||||
*/
|
*/
|
||||||
protected void requestFirmwareVersionAndSerialNumberIfNotSet() throws LcnException {
|
protected void requestFirmwareVersionAndSerialNumberIfNotSet() throws LcnException {
|
||||||
String serialNumber = getThing().getProperties().get(Thing.PROPERTY_SERIAL_NUMBER);
|
if (readFirmwareVersionFromProperty().isEmpty()) {
|
||||||
if (serialNumber == null || serialNumber.isEmpty()) {
|
|
||||||
LcnAddrMod localModuleAddress = moduleAddress;
|
LcnAddrMod localModuleAddress = moduleAddress;
|
||||||
if (localModuleAddress != null) {
|
if (localModuleAddress != null) {
|
||||||
getPckGatewayHandler().getModInfo(localModuleAddress).requestFirmwareVersion();
|
getPckGatewayHandler().getModInfo(localModuleAddress).requestFirmwareVersion();
|
||||||
@ -167,6 +167,21 @@ public class LcnModuleHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<Integer> readFirmwareVersionFromProperty() {
|
||||||
|
String prop = getThing().getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION);
|
||||||
|
|
||||||
|
if (prop == null || prop.length() != FIRMWARE_VERSION_LENGTH) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Optional.of(Integer.parseInt(prop, 16));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.warn("{}: Serial number property invalid", moduleAddress);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleCommand(ChannelUID channelUid, Command command) {
|
public void handleCommand(ChannelUID channelUid, Command command) {
|
||||||
try {
|
try {
|
||||||
@ -241,11 +256,7 @@ public class LcnModuleHandler extends BaseThingHandler {
|
|||||||
* @param pck the message without line termination
|
* @param pck the message without line termination
|
||||||
*/
|
*/
|
||||||
public void handleStatusMessage(String pck) {
|
public void handleStatusMessage(String pck) {
|
||||||
for (AbstractLcnModuleSubHandler handler : subHandlers.values()) {
|
subHandlers.values().forEach(h -> h.tryParse(pck));
|
||||||
if (handler.tryParse(pck)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
metadataSubHandlers.forEach(h -> h.tryParse(pck));
|
metadataSubHandlers.forEach(h -> h.tryParse(pck));
|
||||||
}
|
}
|
||||||
@ -339,6 +350,15 @@ public class LcnModuleHandler extends BaseThingHandler {
|
|||||||
updateProperty(Thing.PROPERTY_SERIAL_NUMBER, serialNumber);
|
updateProperty(Thing.PROPERTY_SERIAL_NUMBER, serialNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the LCN module's serial number property.
|
||||||
|
*
|
||||||
|
* @param serialNumber the new serial number
|
||||||
|
*/
|
||||||
|
public void updateFirmwareVersionProperty(String firmwareVersion) {
|
||||||
|
updateProperty(Thing.PROPERTY_FIRMWARE_VERSION, firmwareVersion);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when an trigger for this LCN module should be fired to openHAB.
|
* Invoked when an trigger for this LCN module should be fired to openHAB.
|
||||||
*
|
*
|
||||||
@ -384,7 +404,7 @@ public class LcnModuleHandler extends BaseThingHandler {
|
|||||||
LcnAddrMod localModuleAddress = moduleAddress;
|
LcnAddrMod localModuleAddress = moduleAddress;
|
||||||
if (connection != null && localModuleAddress != null) {
|
if (connection != null && localModuleAddress != null) {
|
||||||
getPckGatewayHandler().getModInfo(localModuleAddress).onAck(LcnBindingConstants.CODE_ACK, connection,
|
getPckGatewayHandler().getModInfo(localModuleAddress).onAck(LcnBindingConstants.CODE_ACK, connection,
|
||||||
getPckGatewayHandler().getTimeoutMs(), System.nanoTime());
|
getPckGatewayHandler().getTimeoutMs(), System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
} catch (LcnException e) {
|
} catch (LcnException e) {
|
||||||
logger.warn("Connection or module address not set");
|
logger.warn("Connection or module address not set");
|
||||||
|
|||||||
@ -141,7 +141,7 @@ public class Connection {
|
|||||||
if (modData.containsKey(addr)) {
|
if (modData.containsKey(addr)) {
|
||||||
ModInfo modInfo = modData.get(addr);
|
ModInfo modInfo = modData.get(addr);
|
||||||
if (modInfo != null) {
|
if (modInfo != null) {
|
||||||
modInfo.onAck(code, this, this.settings.getTimeout(), System.nanoTime());
|
modInfo.onAck(code, this, this.settings.getTimeout(), System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +244,8 @@ public class Connection {
|
|||||||
logger.warn("Data loss while writing to channel: {}", settings.getAddress());
|
logger.warn("Data loss while writing to channel: {}", settings.getAddress());
|
||||||
} else {
|
} else {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Sent: {}", new String(data, 0, data.length));
|
logger.trace("Sent: {}",
|
||||||
|
new String(data, 0, data.length, LcnDefs.LCN_ENCODING).trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +314,7 @@ public class Connection {
|
|||||||
void queueDirectly(LcnAddr addr, boolean wantsAck, byte[] data) {
|
void queueDirectly(LcnAddr addr, boolean wantsAck, byte[] data) {
|
||||||
if (!addr.isGroup() && wantsAck) {
|
if (!addr.isGroup() && wantsAck) {
|
||||||
this.updateModuleData((LcnAddrMod) addr).queuePckCommandWithAck(data, this, this.settings.getTimeout(),
|
this.updateModuleData((LcnAddrMod) addr).queuePckCommandWithAck(data, this, this.settings.getTimeout(),
|
||||||
System.nanoTime());
|
System.currentTimeMillis());
|
||||||
} else {
|
} else {
|
||||||
this.queueAndSend(new SendDataPck(addr, false, data));
|
this.queueAndSend(new SendDataPck(addr, false, data));
|
||||||
}
|
}
|
||||||
@ -427,7 +428,7 @@ public class Connection {
|
|||||||
*/
|
*/
|
||||||
public void updateModInfos() {
|
public void updateModInfos() {
|
||||||
synchronized (modData) {
|
synchronized (modData) {
|
||||||
modData.values().forEach(i -> i.update(this, settings.getTimeout(), System.nanoTime()));
|
modData.values().forEach(i -> i.update(this, settings.getTimeout(), System.currentTimeMillis()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public class ConnectionStateSegmentScan extends AbstractConnectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void update() {
|
private void update() {
|
||||||
long currTime = System.nanoTime();
|
long currTime = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
if (statusSegmentScan.shouldSendNextRequest(connection.getSettings().getTimeout(), currTime)) {
|
if (statusSegmentScan.shouldSendNextRequest(connection.getSettings().getTimeout(), currTime)) {
|
||||||
connection.queueDirectly(new LcnAddrGrp(3, 3), false, PckGenerator.segmentCouplerScan());
|
connection.queueDirectly(new LcnAddrGrp(3, 3), false, PckGenerator.segmentCouplerScan());
|
||||||
|
|||||||
@ -61,8 +61,8 @@ public class ModInfo {
|
|||||||
/** The LCN module's address. */
|
/** The LCN module's address. */
|
||||||
private final LcnAddr addr;
|
private final LcnAddr addr;
|
||||||
|
|
||||||
/** Firmware date of the LCN module. -1 means "unknown". */
|
/** Firmware date of the LCN module. */
|
||||||
private int firmwareVersion = -1;
|
private Optional<Integer> firmwareVersion = Optional.empty();
|
||||||
|
|
||||||
/** Firmware version request status. */
|
/** Firmware version request status. */
|
||||||
private final RequestStatus requestFirmwareVersion = new RequestStatus(-1, NUM_TRIES, "Firmware Version");
|
private final RequestStatus requestFirmwareVersion = new RequestStatus(-1, NUM_TRIES, "Firmware Version");
|
||||||
@ -128,7 +128,7 @@ public class ModInfo {
|
|||||||
for (Variable var : Variable.values()) {
|
for (Variable var : Variable.values()) {
|
||||||
if (var != Variable.UNKNOWN) {
|
if (var != Variable.UNKNOWN) {
|
||||||
this.requestStatusVars.put(var, new RequestStatus(MAX_STATUS_POLLED_VALUEAGE_MSEC, NUM_TRIES,
|
this.requestStatusVars.put(var, new RequestStatus(MAX_STATUS_POLLED_VALUEAGE_MSEC, NUM_TRIES,
|
||||||
var.getType() + " " + (var.getNumber() + 1)));
|
addr + " " + var.getType() + " " + (var.getNumber() + 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ public class ModInfo {
|
|||||||
* Triggers a request to retrieve the firmware version of the LCN module, if it is not known, yet.
|
* Triggers a request to retrieve the firmware version of the LCN module, if it is not known, yet.
|
||||||
*/
|
*/
|
||||||
public void requestFirmwareVersion() {
|
public void requestFirmwareVersion() {
|
||||||
if (firmwareVersion == -1) {
|
if (firmwareVersion.isEmpty()) {
|
||||||
requestFirmwareVersion.refresh();
|
requestFirmwareVersion.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,11 +237,11 @@ public class ModInfo {
|
|||||||
* @return if the module has at least 4 threshold registers and 12 variables
|
* @return if the module has at least 4 threshold registers and 12 variables
|
||||||
*/
|
*/
|
||||||
public boolean hasExtendedMeasurementProcessing() {
|
public boolean hasExtendedMeasurementProcessing() {
|
||||||
if (firmwareVersion == -1) {
|
if (firmwareVersion.isEmpty()) {
|
||||||
logger.warn("LCN module firmware version unknown");
|
logger.warn("LCN module firmware version unknown");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return firmwareVersion >= LcnBindingConstants.FIRMWARE_2013;
|
return firmwareVersion.map(v -> v >= LcnBindingConstants.FIRMWARE_2013).orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean update(Connection conn, long timeoutMSec, long currTime, RequestStatus requestStatus, String pck)
|
private boolean update(Connection conn, long timeoutMSec, long currTime, RequestStatus requestStatus, String pck)
|
||||||
@ -277,42 +277,11 @@ public class ModInfo {
|
|||||||
if (update(conn, timeoutMSec, currTime, requestStatusRelays, PckGenerator.requestRelaysStatus())) {
|
if (update(conn, timeoutMSec, currTime, requestStatusRelays, PckGenerator.requestRelaysStatus())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update(conn, timeoutMSec, currTime, requestStatusBinSensors, PckGenerator.requestBinSensorsStatus())) {
|
if (update(conn, timeoutMSec, currTime, requestStatusBinSensors, PckGenerator.requestBinSensorsStatus())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variable requests
|
|
||||||
if (this.firmwareVersion != -1) { // Firmware version is required
|
|
||||||
// Use the chance to remove a failed "typeless variable" request
|
|
||||||
if (lastRequestedVarWithoutTypeInResponse != Variable.UNKNOWN) {
|
|
||||||
RequestStatus requestStatus = requestStatusVars.get(lastRequestedVarWithoutTypeInResponse);
|
|
||||||
if (requestStatus != null && requestStatus.isTimeout(timeoutMSec, currTime)) {
|
|
||||||
lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Variables
|
|
||||||
for (Map.Entry<Variable, RequestStatus> kv : this.requestStatusVars.entrySet()) {
|
|
||||||
RequestStatus requestStatus = kv.getValue();
|
|
||||||
if (requestStatus.shouldSendNextRequest(timeoutMSec, currTime)) {
|
|
||||||
// Detect if we can send immediately or if we have to wait for a "typeless" request first
|
|
||||||
boolean hasTypeInResponse = kv.getKey().hasTypeInResponse(this.firmwareVersion);
|
|
||||||
if (hasTypeInResponse || this.lastRequestedVarWithoutTypeInResponse == Variable.UNKNOWN) {
|
|
||||||
try {
|
|
||||||
conn.queue(this.addr, false,
|
|
||||||
PckGenerator.requestVarStatus(kv.getKey(), this.firmwareVersion));
|
|
||||||
requestStatus.onRequestSent(currTime);
|
|
||||||
if (!hasTypeInResponse) {
|
|
||||||
this.lastRequestedVarWithoutTypeInResponse = kv.getKey();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} catch (LcnException ex) {
|
|
||||||
requestStatus.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (update(conn, timeoutMSec, currTime, requestStatusLedsAndLogicOps,
|
if (update(conn, timeoutMSec, currTime, requestStatusLedsAndLogicOps,
|
||||||
PckGenerator.requestLedsAndLogicOpsStatus())) {
|
PckGenerator.requestLedsAndLogicOpsStatus())) {
|
||||||
return;
|
return;
|
||||||
@ -322,6 +291,45 @@ public class ModInfo {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Variable requests
|
||||||
|
firmwareVersion.ifPresent(firmwareVersion -> { // Firmware version is required
|
||||||
|
// Use the chance to remove a failed "typeless variable" request
|
||||||
|
if (lastRequestedVarWithoutTypeInResponse != Variable.UNKNOWN) {
|
||||||
|
RequestStatus requestStatus = requestStatusVars.get(lastRequestedVarWithoutTypeInResponse);
|
||||||
|
if (requestStatus != null && requestStatus.isTimeout(timeoutMSec, currTime)) {
|
||||||
|
lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Variables
|
||||||
|
for (Map.Entry<Variable, RequestStatus> kv : this.requestStatusVars.entrySet()) {
|
||||||
|
RequestStatus requestStatus = kv.getValue();
|
||||||
|
try {
|
||||||
|
if (requestStatus.shouldSendNextRequest(timeoutMSec, currTime)) {
|
||||||
|
// Detect if we can send immediately or if we have to wait for a "typeless" request first
|
||||||
|
boolean hasTypeInResponse = kv.getKey().hasTypeInResponse(firmwareVersion);
|
||||||
|
if (hasTypeInResponse || this.lastRequestedVarWithoutTypeInResponse == Variable.UNKNOWN) {
|
||||||
|
try {
|
||||||
|
conn.queue(this.addr, false,
|
||||||
|
PckGenerator.requestVarStatus(kv.getKey(), firmwareVersion));
|
||||||
|
requestStatus.onRequestSent(currTime);
|
||||||
|
if (!hasTypeInResponse) {
|
||||||
|
this.lastRequestedVarWithoutTypeInResponse = kv.getKey();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (LcnException ex) {
|
||||||
|
logger.warn("{}: Failed to generate PCK message: {}: {}", addr, kv.getKey(),
|
||||||
|
ex.getMessage());
|
||||||
|
requestStatus.reset();
|
||||||
|
lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (LcnException e) {
|
||||||
|
logger.warn("{}: Failed to receive measurement value: {}", addr, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Try to send next acknowledged command. Will also detect failed ones.
|
// Try to send next acknowledged command. Will also detect failed ones.
|
||||||
this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime);
|
this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime);
|
||||||
} catch (LcnException e) {
|
} catch (LcnException e) {
|
||||||
@ -334,8 +342,8 @@ public class ModInfo {
|
|||||||
*
|
*
|
||||||
* @return the date
|
* @return the date
|
||||||
*/
|
*/
|
||||||
public int getFirmwareVersion() {
|
public Optional<Integer> getFirmwareVersion() {
|
||||||
return this.firmwareVersion;
|
return firmwareVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -344,7 +352,7 @@ public class ModInfo {
|
|||||||
* @param firmwareVersion the date
|
* @param firmwareVersion the date
|
||||||
*/
|
*/
|
||||||
public void setFirmwareVersion(int firmwareVersion) {
|
public void setFirmwareVersion(int firmwareVersion) {
|
||||||
this.firmwareVersion = firmwareVersion;
|
this.firmwareVersion = Optional.of(firmwareVersion);
|
||||||
|
|
||||||
requestFirmwareVersion.onResponseReceived();
|
requestFirmwareVersion.onResponseReceived();
|
||||||
|
|
||||||
@ -430,7 +438,7 @@ public class ModInfo {
|
|||||||
* Requests the current value of all LEDs and logic operations, after a LED has been changed by openHAB.
|
* Requests the current value of all LEDs and logic operations, after a LED has been changed by openHAB.
|
||||||
*/
|
*/
|
||||||
public void refreshStatusLedsAnLogicAfterChange() {
|
public void refreshStatusLedsAnLogicAfterChange() {
|
||||||
requestStatusLedsAndLogicOps.nextRequestIn(STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC, System.nanoTime());
|
requestStatusLedsAndLogicOps.nextRequestIn(STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -444,7 +452,7 @@ public class ModInfo {
|
|||||||
* Requests the current locking states of all keys, after a lock state has been changed by openHAB.
|
* Requests the current locking states of all keys, after a lock state has been changed by openHAB.
|
||||||
*/
|
*/
|
||||||
public void refreshStatusStatusLockedKeysAfterChange() {
|
public void refreshStatusStatusLockedKeysAfterChange() {
|
||||||
requestStatusLockedKeys.nextRequestIn(STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC, System.nanoTime());
|
requestStatusLockedKeys.nextRequestIn(STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -480,6 +488,10 @@ public class ModInfo {
|
|||||||
if (requestStatus != null) {
|
if (requestStatus != null) {
|
||||||
requestStatus.onResponseReceived();
|
requestStatus.onResponseReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (variable == lastRequestedVarWithoutTypeInResponse) {
|
||||||
|
lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN; // Reset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -495,4 +507,11 @@ public class ModInfo {
|
|||||||
public void onLockedKeysResponseReceived() {
|
public void onLockedKeysResponseReceived() {
|
||||||
requestStatusLockedKeys.onResponseReceived();
|
requestStatusLockedKeys.onResponseReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the module's bus address.
|
||||||
|
*/
|
||||||
|
public LcnAddr getAddress() {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,7 +92,7 @@ public class RequestStatus {
|
|||||||
* @return true if request timed out
|
* @return true if request timed out
|
||||||
*/
|
*/
|
||||||
synchronized boolean isTimeout(long timeoutMSec, long currTime) {
|
synchronized boolean isTimeout(long timeoutMSec, long currTime) {
|
||||||
return this.isPending() && currTime - this.currRequestTimeStamp >= timeoutMSec * 1000000L;
|
return this.isPending() && currTime - this.currRequestTimeStamp >= timeoutMSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,14 +114,14 @@ public class RequestStatus {
|
|||||||
*/
|
*/
|
||||||
public synchronized void nextRequestIn(long delayMSec, long currTime) {
|
public synchronized void nextRequestIn(long delayMSec, long currTime) {
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.nextRequestTimeStamp = currTime + delayMSec * 1000000L;
|
this.nextRequestTimeStamp = currTime + delayMSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules a request to retrieve the current value.
|
* Schedules a request to retrieve the current value.
|
||||||
*/
|
*/
|
||||||
public synchronized void refresh() {
|
public synchronized void refresh() {
|
||||||
nextRequestIn(0, System.nanoTime());
|
nextRequestIn(0, System.currentTimeMillis());
|
||||||
this.numRetriesLeft = this.numTries;
|
this.numRetriesLeft = this.numTries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +181,11 @@ public class RequestStatus {
|
|||||||
public synchronized void onResponseReceived() {
|
public synchronized void onResponseReceived() {
|
||||||
if (this.isActive) {
|
if (this.isActive) {
|
||||||
this.currRequestTimeStamp = 0; // Mark request (if any) as successful
|
this.currRequestTimeStamp = 0; // Mark request (if any) as successful
|
||||||
|
|
||||||
|
// Reset timer for next transmission
|
||||||
|
if (this.maxAgeMSec != -1) {
|
||||||
|
this.nextRequestIn(this.maxAgeMSec, System.currentTimeMillis());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -120,7 +120,7 @@ public abstract class AbstractLcnModuleSubHandler implements ILcnModuleSubHandle
|
|||||||
* @param pck the message to process
|
* @param pck the message to process
|
||||||
* @return true, if the message could be processed successfully
|
* @return true, if the message could be processed successfully
|
||||||
*/
|
*/
|
||||||
public boolean tryParse(String pck) {
|
public void tryParse(String pck) {
|
||||||
Optional<Matcher> firstSuccessfulMatcher = getPckStatusMessagePatterns().stream().map(p -> p.matcher(pck))
|
Optional<Matcher> firstSuccessfulMatcher = getPckStatusMessagePatterns().stream().map(p -> p.matcher(pck))
|
||||||
.filter(Matcher::matches).filter(m -> handler.isMyAddress(m.group("segId"), m.group("modId")))
|
.filter(Matcher::matches).filter(m -> handler.isMyAddress(m.group("segId"), m.group("modId")))
|
||||||
.findAny();
|
.findAny();
|
||||||
@ -132,8 +132,6 @@ public abstract class AbstractLcnModuleSubHandler implements ILcnModuleSubHandle
|
|||||||
logger.warn("Parse error: {}", e.getMessage());
|
logger.warn("Parse error: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return firstSuccessfulMatcher.isPresent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -38,7 +38,6 @@ public abstract class AbstractLcnModuleVariableSubHandler extends AbstractLcnMod
|
|||||||
@Override
|
@Override
|
||||||
public void handleRefresh(LcnChannelGroup channelGroup, int number) {
|
public void handleRefresh(LcnChannelGroup channelGroup, int number) {
|
||||||
requestVariable(info, channelGroup, number);
|
requestVariable(info, channelGroup, number);
|
||||||
info.requestFirmwareVersion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -47,6 +47,7 @@ public class LcnModuleMetaFirmwareSubHandler extends AbstractLcnModuleSubHandler
|
|||||||
public void handleStatusMessage(Matcher matcher) {
|
public void handleStatusMessage(Matcher matcher) {
|
||||||
info.setFirmwareVersion(Integer.parseInt(matcher.group("firmwareVersion"), 16));
|
info.setFirmwareVersion(Integer.parseInt(matcher.group("firmwareVersion"), 16));
|
||||||
handler.updateSerialNumberProperty(matcher.group("sn"));
|
handler.updateSerialNumberProperty(matcher.group("sn"));
|
||||||
|
handler.updateFirmwareVersionProperty(matcher.group("firmwareVersion"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -107,7 +107,7 @@ public class LcnModuleOutputSubHandler extends AbstractLcnModuleSubHandler {
|
|||||||
currentColor = hsbType;
|
currentColor = hsbType;
|
||||||
handler.updateChannel(LcnChannelGroup.OUTPUT, OUTPUT_COLOR, currentColor);
|
handler.updateChannel(LcnChannelGroup.OUTPUT, OUTPUT_COLOR, currentColor);
|
||||||
|
|
||||||
if (info.getFirmwareVersion() >= LcnBindingConstants.FIRMWARE_2014) {
|
if (info.getFirmwareVersion().map(v -> v >= LcnBindingConstants.FIRMWARE_2014).orElse(true)) {
|
||||||
handler.sendPck(PckGenerator.dimAllOutputs(currentColor.getRed().doubleValue(),
|
handler.sendPck(PckGenerator.dimAllOutputs(currentColor.getRed().doubleValue(),
|
||||||
currentColor.getGreen().doubleValue(), currentColor.getBlue().doubleValue(), output4.doubleValue(),
|
currentColor.getGreen().doubleValue(), currentColor.getBlue().doubleValue(), output4.doubleValue(),
|
||||||
COLOR_RAMP_MS));
|
COLOR_RAMP_MS));
|
||||||
|
|||||||
@ -49,7 +49,7 @@ public class LcnModuleRvarLockSubHandler extends AbstractLcnModuleVariableSubHan
|
|||||||
|
|
||||||
// request new lock state, if the module doesn't send it on itself
|
// request new lock state, if the module doesn't send it on itself
|
||||||
Variable variable = getVariable(LcnChannelGroup.RVARSETPOINT, number);
|
Variable variable = getVariable(LcnChannelGroup.RVARSETPOINT, number);
|
||||||
if (variable.shouldPollStatusAfterRegulatorLock(info.getFirmwareVersion(), locked)) {
|
if (info.getFirmwareVersion().map(v -> variable.shouldPollStatusAfterRegulatorLock(v, locked)).orElse(true)) {
|
||||||
info.refreshVariable(variable);
|
info.refreshVariable(variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
package org.openhab.binding.lcn.internal.subhandler;
|
package org.openhab.binding.lcn.internal.subhandler;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -25,10 +25,13 @@ import org.openhab.binding.lcn.internal.common.LcnDefs;
|
|||||||
import org.openhab.binding.lcn.internal.common.LcnException;
|
import org.openhab.binding.lcn.internal.common.LcnException;
|
||||||
import org.openhab.binding.lcn.internal.common.PckGenerator;
|
import org.openhab.binding.lcn.internal.common.PckGenerator;
|
||||||
import org.openhab.binding.lcn.internal.common.Variable;
|
import org.openhab.binding.lcn.internal.common.Variable;
|
||||||
|
import org.openhab.binding.lcn.internal.common.Variable.Type;
|
||||||
import org.openhab.binding.lcn.internal.common.VariableValue;
|
import org.openhab.binding.lcn.internal.common.VariableValue;
|
||||||
import org.openhab.binding.lcn.internal.connection.ModInfo;
|
import org.openhab.binding.lcn.internal.connection.ModInfo;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles Commands and State changes of regulator setpoints of an LCN module.
|
* Handles Commands and State changes of regulator setpoints of an LCN module.
|
||||||
@ -37,8 +40,9 @@ import org.openhab.core.library.types.OnOffType;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class LcnModuleRvarSetpointSubHandler extends AbstractLcnModuleVariableSubHandler {
|
public class LcnModuleRvarSetpointSubHandler extends AbstractLcnModuleVariableSubHandler {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(LcnModuleRvarSetpointSubHandler.class);
|
||||||
private static final Pattern PATTERN = Pattern
|
private static final Pattern PATTERN = Pattern
|
||||||
.compile(LcnBindingConstants.ADDRESS_REGEX + "\\.S(?<id>\\d)(?<value>\\d+)");
|
.compile(LcnBindingConstants.ADDRESS_REGEX + "\\.S(?<id>\\d)(?<value>\\d{1,5})");
|
||||||
|
|
||||||
public LcnModuleRvarSetpointSubHandler(LcnModuleHandler handler, ModInfo info) {
|
public LcnModuleRvarSetpointSubHandler(LcnModuleHandler handler, ModInfo info) {
|
||||||
super(handler, info);
|
super(handler, info);
|
||||||
@ -66,7 +70,19 @@ public class LcnModuleRvarSetpointSubHandler extends AbstractLcnModuleVariableSu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleStatusMessage(Matcher matcher) throws LcnException {
|
public void handleStatusMessage(Matcher matcher) throws LcnException {
|
||||||
Variable variable = Variable.setPointIdToVar(Integer.parseInt(matcher.group("id")) - 1);
|
Variable variable;
|
||||||
|
if (matcher.pattern() == PATTERN) {
|
||||||
|
variable = Variable.setPointIdToVar(Integer.parseInt(matcher.group("id")) - 1);
|
||||||
|
} else if (matcher.pattern() == LcnBindingConstants.MEASUREMENT_PATTERN_BEFORE_2013) {
|
||||||
|
variable = info.getLastRequestedVarWithoutTypeInResponse();
|
||||||
|
|
||||||
|
if (variable.getType() != Type.REGULATOR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("Unexpected pattern: {}", matcher.pattern());
|
||||||
|
return;
|
||||||
|
}
|
||||||
VariableValue value = fireUpdateAndReset(matcher, "", variable);
|
VariableValue value = fireUpdateAndReset(matcher, "", variable);
|
||||||
|
|
||||||
fireUpdate(LcnChannelGroup.RVARLOCK, variable.getNumber(), OnOffType.from(value.isRegulatorLocked()));
|
fireUpdate(LcnChannelGroup.RVARLOCK, variable.getNumber(), OnOffType.from(value.isRegulatorLocked()));
|
||||||
@ -74,6 +90,6 @@ public class LcnModuleRvarSetpointSubHandler extends AbstractLcnModuleVariableSu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Pattern> getPckStatusMessagePatterns() {
|
public Collection<Pattern> getPckStatusMessagePatterns() {
|
||||||
return Collections.singleton(PATTERN);
|
return List.of(PATTERN, LcnBindingConstants.MEASUREMENT_PATTERN_BEFORE_2013);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@ public class LcnModuleThresholdSubHandler extends AbstractLcnModuleVariableSubHa
|
|||||||
info.hasExtendedMeasurementProcessing()));
|
info.hasExtendedMeasurementProcessing()));
|
||||||
|
|
||||||
// request new value, if the module doesn't send it on itself
|
// request new value, if the module doesn't send it on itself
|
||||||
if (variable.shouldPollStatusAfterCommand(info.getFirmwareVersion())) {
|
if (info.getFirmwareVersion().map(v -> variable.shouldPollStatusAfterCommand(v)).orElse(true)) {
|
||||||
info.refreshVariable(variable);
|
info.refreshVariable(variable);
|
||||||
}
|
}
|
||||||
} catch (LcnException e) {
|
} catch (LcnException e) {
|
||||||
|
|||||||
@ -12,8 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.lcn.internal.subhandler;
|
package org.openhab.binding.lcn.internal.subhandler;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ import org.openhab.binding.lcn.internal.common.LcnDefs;
|
|||||||
import org.openhab.binding.lcn.internal.common.LcnException;
|
import org.openhab.binding.lcn.internal.common.LcnException;
|
||||||
import org.openhab.binding.lcn.internal.common.PckGenerator;
|
import org.openhab.binding.lcn.internal.common.PckGenerator;
|
||||||
import org.openhab.binding.lcn.internal.common.Variable;
|
import org.openhab.binding.lcn.internal.common.Variable;
|
||||||
|
import org.openhab.binding.lcn.internal.common.Variable.Type;
|
||||||
import org.openhab.binding.lcn.internal.connection.ModInfo;
|
import org.openhab.binding.lcn.internal.connection.ModInfo;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -40,8 +41,6 @@ public class LcnModuleVariableSubHandler extends AbstractLcnModuleVariableSubHan
|
|||||||
private final Logger logger = LoggerFactory.getLogger(LcnModuleVariableSubHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(LcnModuleVariableSubHandler.class);
|
||||||
private static final Pattern PATTERN = Pattern
|
private static final Pattern PATTERN = Pattern
|
||||||
.compile(LcnBindingConstants.ADDRESS_REGEX + "\\.A(?<id>\\d{3})(?<value>\\d+)");
|
.compile(LcnBindingConstants.ADDRESS_REGEX + "\\.A(?<id>\\d{3})(?<value>\\d+)");
|
||||||
private static final Pattern PATTERN_LEGACY = Pattern
|
|
||||||
.compile(LcnBindingConstants.ADDRESS_REGEX + "\\.(?<value>\\d+)");
|
|
||||||
|
|
||||||
public LcnModuleVariableSubHandler(LcnModuleHandler handler, ModInfo info) {
|
public LcnModuleVariableSubHandler(LcnModuleHandler handler, ModInfo info) {
|
||||||
super(handler, info);
|
super(handler, info);
|
||||||
@ -56,7 +55,7 @@ public class LcnModuleVariableSubHandler extends AbstractLcnModuleVariableSubHan
|
|||||||
handler.sendPck(PckGenerator.setVariableRelative(variable, LcnDefs.RelVarRef.CURRENT, relativeChange));
|
handler.sendPck(PckGenerator.setVariableRelative(variable, LcnDefs.RelVarRef.CURRENT, relativeChange));
|
||||||
|
|
||||||
// request new value, if the module doesn't send it on itself
|
// request new value, if the module doesn't send it on itself
|
||||||
if (variable.shouldPollStatusAfterCommand(info.getFirmwareVersion())) {
|
if (info.getFirmwareVersion().map(v -> variable.shouldPollStatusAfterCommand(v)).orElse(true)) {
|
||||||
info.refreshVariable(variable);
|
info.refreshVariable(variable);
|
||||||
}
|
}
|
||||||
} catch (LcnException e) {
|
} catch (LcnException e) {
|
||||||
@ -71,9 +70,12 @@ public class LcnModuleVariableSubHandler extends AbstractLcnModuleVariableSubHan
|
|||||||
Variable variable;
|
Variable variable;
|
||||||
if (matcher.pattern() == PATTERN) {
|
if (matcher.pattern() == PATTERN) {
|
||||||
variable = Variable.varIdToVar(Integer.parseInt(matcher.group("id")) - 1);
|
variable = Variable.varIdToVar(Integer.parseInt(matcher.group("id")) - 1);
|
||||||
} else if (matcher.pattern() == PATTERN_LEGACY) {
|
} else if (matcher.pattern() == LcnBindingConstants.MEASUREMENT_PATTERN_BEFORE_2013) {
|
||||||
variable = info.getLastRequestedVarWithoutTypeInResponse();
|
variable = info.getLastRequestedVarWithoutTypeInResponse();
|
||||||
info.setLastRequestedVarWithoutTypeInResponse(Variable.UNKNOWN); // Reset
|
|
||||||
|
if (variable == Variable.UNKNOWN || variable.getType() != Type.VARIABLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Unexpected pattern: {}", matcher.pattern());
|
logger.warn("Unexpected pattern: {}", matcher.pattern());
|
||||||
return;
|
return;
|
||||||
@ -83,6 +85,6 @@ public class LcnModuleVariableSubHandler extends AbstractLcnModuleVariableSubHan
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Pattern> getPckStatusMessagePatterns() {
|
public Collection<Pattern> getPckStatusMessagePatterns() {
|
||||||
return Arrays.asList(PATTERN, PATTERN_LEGACY);
|
return List.of(PATTERN, LcnBindingConstants.MEASUREMENT_PATTERN_BEFORE_2013);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user