Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[insteon] Add debug monitoring support for modem and x10 devices #18048

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.insteon.internal.device.DeviceAddress;
import org.openhab.binding.insteon.internal.device.InsteonAddress;
import org.openhab.binding.insteon.internal.device.InsteonScene;
import org.openhab.binding.insteon.internal.device.X10Address;
Expand Down Expand Up @@ -82,19 +84,20 @@ private static enum MessageType {

private boolean monitoring = false;
private boolean monitorAllDevices = false;
private Set<InsteonAddress> monitoredAddresses = new HashSet<>();
private Set<DeviceAddress> monitoredAddresses = new HashSet<>();
private @Nullable X10Address lastX10Address;

public DebugCommand(InsteonCommandExtension commandExtension) {
super(NAME, DESCRIPTION, commandExtension);
}

@Override
public List<String> getUsages() {
return List.of(buildCommandUsage(LIST_MONITORED, "list monitored device(s)"),
return List.of(buildCommandUsage(LIST_MONITORED, "list monitored Insteon/X10 device(s)"),
buildCommandUsage(START_MONITORING + " " + ALL_OPTION + "|<address>",
"start logging message events for device(s) in separate file(s)"),
"start logging message events for Insteon/X10 device(s) in separate file(s)"),
buildCommandUsage(STOP_MONITORING + " " + ALL_OPTION + "|<address>",
"stop logging message events for device(s) in separate file(s)"),
"stop logging message events for Insteon/X10 device(s) in separate file(s)"),
buildCommandUsage(SEND_BROADCAST_MESSAGE + " <group> <cmd1> <cmd2>",
"send an Insteon broadcast message to a group"),
buildCommandUsage(SEND_STANDARD_MESSAGE + " <address> <cmd1> <cmd2>",
Expand Down Expand Up @@ -196,14 +199,15 @@ public boolean complete(String[] args, int cursorArgumentIndex, int cursorPositi
case START_MONITORING:
strings = monitorAllDevices ? List.of()
: Stream.concat(Stream.of(ALL_OPTION),
getModem().getDB().getDevices().stream()
Stream.concat(Stream.of(getModem().getAddress()),
getModem().getDB().getDevices().stream())
.filter(address -> !monitoredAddresses.contains(address))
.map(InsteonAddress::toString))
.toList();
break;
case STOP_MONITORING:
strings = monitorAllDevices ? List.of(ALL_OPTION)
: monitoredAddresses.stream().map(InsteonAddress::toString).toList();
: monitoredAddresses.stream().map(DeviceAddress::toString).toList();
break;
case SEND_BROADCAST_MESSAGE:
strings = getModem().getDB().getBroadcastGroups().stream().map(String::valueOf).toList();
Expand Down Expand Up @@ -235,54 +239,65 @@ public void disconnected() {

@Override
public void messageReceived(Msg msg) {
try {
InsteonAddress address = msg.getInsteonAddress(msg.isReply() ? "toAddress" : "fromAddress");
if (monitorAllDevices || monitoredAddresses.contains(address)) {
logMessageEvent(address, msg);
}
} catch (FieldException ignored) {
// ignore message with no address field
}
logMessageEvent(msg);
}

@Override
public void messageSent(Msg msg) {
try {
InsteonAddress address = msg.getInsteonAddress("toAddress");
if (monitorAllDevices || monitoredAddresses.contains(address)) {
logMessageEvent(address, msg);
logMessageEvent(msg);
}

private DeviceAddress getMsgEventAddress(Msg msg) throws FieldException {
if (msg.isX10()) {
X10Address address = msg.isX10Address() ? msg.getX10Address() : lastX10Address;
if (address == null) {
throw new FieldException("unknown x10 address");
}
} catch (FieldException ignored) {
// ignore message with no address field
lastX10Address = address;
return address;
} else if (msg.isInsteon()) {
return msg.isInbound() && !msg.isReply() ? msg.getInsteonAddress("fromAddress")
: !msg.isAllLinkBroadcast() ? msg.getInsteonAddress("toAddress") : getModem().getAddress();
} else {
return getModem().getAddress();
}
}

private Path getMsgEventsFilePath(String address) {
return getBindingDataFilePath(MSG_EVENTS_FILE_PREFIX + "-" + address.toLowerCase().replace(".", "") + ".log");
private Path getMsgEventsFilePath(DeviceAddress address) {
String name = address.toString().toLowerCase().replace(".", "");
if (address instanceof X10Address) {
name = "x10-" + name;
}
return getBindingDataFilePath(MSG_EVENTS_FILE_PREFIX + "-" + name + ".log");
}

private void clearMonitorFiles(String address) {
String prefix = ALL_OPTION.equals(address) ? MSG_EVENTS_FILE_PREFIX
private void clearMonitorFiles(@Nullable DeviceAddress address) {
String prefix = address == null ? MSG_EVENTS_FILE_PREFIX
: getMsgEventsFilePath(address).getFileName().toString();

getBindingDataFilePaths(prefix).map(Path::toFile).forEach(File::delete);
}

private void logMessageEvent(InsteonAddress address, Msg msg) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
String line = timestamp + " " + msg + System.lineSeparator();
Path path = getMsgEventsFilePath(address.toString());

private void logMessageEvent(Msg msg) {
try {
Files.createDirectories(path.getParent());
Files.writeString(path, line, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
DeviceAddress address = getMsgEventAddress(msg);
if (monitorAllDevices || monitoredAddresses.contains(address)) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
String line = timestamp + " " + msg + System.lineSeparator();
Path path = getMsgEventsFilePath(address);

Files.createDirectories(path.getParent());
Files.writeString(path, line, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
} catch (FieldException e) {
logger.warn("failed to parse message", e);
} catch (IOException e) {
logger.warn("failed to write to message event file", e);
}
}

private void listMonitoredDevices(Console console) {
String addresses = monitoredAddresses.stream().map(InsteonAddress::toString).collect(Collectors.joining(", "));
String addresses = monitoredAddresses.stream().map(DeviceAddress::toString).collect(Collectors.joining(", "));
if (!addresses.isEmpty()) {
console.println("The monitored device(s) are: " + addresses);
} else if (monitorAllDevices) {
Expand All @@ -292,30 +307,27 @@ private void listMonitoredDevices(Console console) {
}
}

private void startMonitoring(Console console, String address) {
if (ALL_OPTION.equals(address)) {
if (!monitorAllDevices) {
monitorAllDevices = true;
monitoredAddresses.clear();
console.println("Started monitoring all devices.");
console.println("Message events logged in " + getBindingDataDirPath());
clearMonitorFiles(address);
} else {
console.println("Already monitoring all devices.");
}
} else if (InsteonAddress.isValid(address)) {
if (monitorAllDevices) {
console.println("Already monitoring all devices.");
} else if (monitoredAddresses.add(new InsteonAddress(address))) {
private void startMonitoring(Console console, String arg) {
if (monitorAllDevices) {
console.println("Already monitoring all devices.");
} else if (ALL_OPTION.equals(arg)) {
monitorAllDevices = true;
monitoredAddresses.clear();
console.println("Started monitoring all devices.");
console.println("Message events logged in " + getBindingDataDirPath());
clearMonitorFiles(null);
} else {
DeviceAddress address = InsteonAddress.isValid(arg) ? new InsteonAddress(arg)
: X10Address.isValid(arg) ? new X10Address(arg) : null;
if (address == null) {
console.println("Invalid device address argument: " + arg);
} else if (monitoredAddresses.add(address)) {
console.println("Started monitoring the device " + address + ".");
console.println("Message events logged in " + getMsgEventsFilePath(address));
clearMonitorFiles(address);
} else {
console.println("Already monitoring the device " + address + ".");
}
} else {
console.println("Invalid device address" + address + ".");
return;
}

if (!monitoring) {
Expand All @@ -324,31 +336,26 @@ private void startMonitoring(Console console, String address) {
}
}

private void stopMonitoring(Console console, String address) {
if (!monitoring) {
console.println("Not monitoring any devices.");
return;
}

if (ALL_OPTION.equals(address)) {
private void stopMonitoring(Console console, String arg) {
if (ALL_OPTION.equals(arg)) {
if (monitorAllDevices) {
monitorAllDevices = false;
console.println("Stopped monitoring all devices.");
} else {
console.println("Not monitoring all devices.");
}
} else if (InsteonAddress.isValid(address)) {
} else {
DeviceAddress address = InsteonAddress.isValid(arg) ? new InsteonAddress(arg)
: X10Address.isValid(arg) ? new X10Address(arg) : null;
if (monitorAllDevices) {
console.println("Not monitoring individual devices.");
} else if (monitoredAddresses.remove(new InsteonAddress(address))) {
} else if (address == null) {
console.println("Invalid device address argument: " + arg);
} else if (monitoredAddresses.remove(address)) {
console.println("Stopped monitoring the device " + address + ".");
} else {
console.println("Not monitoring the device " + address + ".");
return;
}
} else {
console.println("Invalid address device address " + address + ".");
return;
}

if (!monitorAllDevices && monitoredAddresses.isEmpty()) {
Expand Down