mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[insteon] Add modem database backup restore console commands (#17958)
Signed-off-by: jsetton <jeremy.setton@gmail.com>
This commit is contained in:
parent
36cff511b9
commit
d54a51b732
@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.insteon.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -34,7 +34,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
@NonNullByDefault
|
||||
public class InsteonBindingConstants {
|
||||
public static final String BINDING_ID = "insteon";
|
||||
public static final String BINDING_DATA_DIR = OpenHAB.getUserDataFolder() + File.separator + BINDING_ID;
|
||||
public static final Path BINDING_DATA_DIR = Path.of(OpenHAB.getUserDataFolder(), BINDING_ID);
|
||||
|
||||
// List of all thing type uids
|
||||
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
|
||||
|
@ -13,11 +13,11 @@
|
||||
package org.openhab.binding.insteon.internal.command;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -27,7 +27,6 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.insteon.internal.InsteonBindingConstants;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonAddress;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonScene;
|
||||
import org.openhab.binding.insteon.internal.device.X10Address;
|
||||
@ -71,7 +70,7 @@ public class DebugCommand extends InsteonCommand implements PortListener {
|
||||
|
||||
private static final String ALL_OPTION = "--all";
|
||||
|
||||
private static final String MSG_EVENTS_FILE_PREFIX = "messageEvents";
|
||||
private static final String MSG_EVENTS_FILE_PREFIX = "message-events";
|
||||
|
||||
private static enum MessageType {
|
||||
STANDARD,
|
||||
@ -195,9 +194,16 @@ public class DebugCommand extends InsteonCommand implements PortListener {
|
||||
} else if (cursorArgumentIndex == 1) {
|
||||
switch (args[0]) {
|
||||
case START_MONITORING:
|
||||
strings = monitorAllDevices ? List.of()
|
||||
: Stream.concat(Stream.of(ALL_OPTION),
|
||||
getModem().getDB().getDevices().stream()
|
||||
.filter(address -> !monitoredAddresses.contains(address))
|
||||
.map(InsteonAddress::toString))
|
||||
.toList();
|
||||
break;
|
||||
case STOP_MONITORING:
|
||||
strings = Stream.concat(Stream.of(ALL_OPTION),
|
||||
getModem().getDB().getDevices().stream().map(InsteonAddress::toString)).toList();
|
||||
strings = monitorAllDevices ? List.of(ALL_OPTION)
|
||||
: monitoredAddresses.stream().map(InsteonAddress::toString).toList();
|
||||
break;
|
||||
case SEND_BROADCAST_MESSAGE:
|
||||
strings = getModem().getDB().getBroadcastGroups().stream().map(String::valueOf).toList();
|
||||
@ -251,40 +257,25 @@ public class DebugCommand extends InsteonCommand implements PortListener {
|
||||
}
|
||||
}
|
||||
|
||||
private String getMsgEventsFileName(String address) {
|
||||
return MSG_EVENTS_FILE_PREFIX + "-" + address.replace(".", "") + ".log";
|
||||
}
|
||||
|
||||
private String getMsgEventsFilePath(String address) {
|
||||
return InsteonBindingConstants.BINDING_DATA_DIR + File.separator + getMsgEventsFileName(address);
|
||||
private Path getMsgEventsFilePath(String address) {
|
||||
return getBindingDataFilePath(MSG_EVENTS_FILE_PREFIX + "-" + address.toLowerCase().replace(".", "") + ".log");
|
||||
}
|
||||
|
||||
private void clearMonitorFiles(String address) {
|
||||
File folder = new File(InsteonBindingConstants.BINDING_DATA_DIR);
|
||||
String prefix = ALL_OPTION.equals(address) ? MSG_EVENTS_FILE_PREFIX : getMsgEventsFileName(address);
|
||||
String prefix = ALL_OPTION.equals(address) ? MSG_EVENTS_FILE_PREFIX
|
||||
: getMsgEventsFilePath(address).getFileName().toString();
|
||||
|
||||
if (folder.isDirectory()) {
|
||||
Arrays.asList(folder.listFiles()).stream().filter(file -> file.getName().startsWith(prefix))
|
||||
.forEach(File::delete);
|
||||
}
|
||||
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 pathname = getMsgEventsFilePath(address.toString());
|
||||
String line = timestamp + " " + msg + System.lineSeparator();
|
||||
Path path = getMsgEventsFilePath(address.toString());
|
||||
|
||||
try {
|
||||
File file = new File(pathname);
|
||||
File parent = file.getParentFile();
|
||||
if (parent == null) {
|
||||
throw new IOException(pathname + " does not name a parent directory");
|
||||
}
|
||||
parent.mkdirs();
|
||||
file.createNewFile();
|
||||
|
||||
PrintStream ps = new PrintStream(new FileOutputStream(file, true));
|
||||
ps.println(timestamp + " " + msg.toString());
|
||||
ps.close();
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.writeString(path, line, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
|
||||
} catch (IOException e) {
|
||||
logger.warn("failed to write to message event file", e);
|
||||
}
|
||||
@ -307,7 +298,7 @@ public class DebugCommand extends InsteonCommand implements PortListener {
|
||||
monitorAllDevices = true;
|
||||
monitoredAddresses.clear();
|
||||
console.println("Started monitoring all devices.");
|
||||
console.println("Message events logged in " + InsteonBindingConstants.BINDING_DATA_DIR);
|
||||
console.println("Message events logged in " + getBindingDataDirPath());
|
||||
clearMonitorFiles(address);
|
||||
} else {
|
||||
console.println("Already monitoring all devices.");
|
||||
|
@ -12,6 +12,9 @@
|
||||
*/
|
||||
package org.openhab.binding.insteon.internal.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@ -20,6 +23,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.insteon.internal.InsteonBindingConstants;
|
||||
import org.openhab.binding.insteon.internal.device.Device;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonAddress;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonDevice;
|
||||
@ -171,4 +175,21 @@ public abstract class InsteonCommand implements ConsoleCommandCompleter {
|
||||
InsteonDevice device = getInsteonDevice(thingId);
|
||||
return device != null ? device.getInsteonEngine() : InsteonEngine.UNKNOWN;
|
||||
}
|
||||
|
||||
protected Path getBindingDataDirPath() {
|
||||
return InsteonBindingConstants.BINDING_DATA_DIR;
|
||||
}
|
||||
|
||||
protected Path getBindingDataFilePath(String filename) {
|
||||
return InsteonBindingConstants.BINDING_DATA_DIR.resolve(filename);
|
||||
}
|
||||
|
||||
protected Stream<Path> getBindingDataFilePaths(String prefix) {
|
||||
try {
|
||||
return Files.list(InsteonBindingConstants.BINDING_DATA_DIR)
|
||||
.filter(path -> path.getFileName().toString().startsWith(prefix));
|
||||
} catch (IOException e) {
|
||||
return Stream.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,12 @@
|
||||
*/
|
||||
package org.openhab.binding.insteon.internal.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@ -20,6 +26,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonAddress;
|
||||
import org.openhab.binding.insteon.internal.device.ProductData;
|
||||
import org.openhab.binding.insteon.internal.device.database.ModemDBChange;
|
||||
import org.openhab.binding.insteon.internal.device.database.ModemDBEntry;
|
||||
import org.openhab.binding.insteon.internal.device.database.ModemDBRecord;
|
||||
import org.openhab.binding.insteon.internal.handler.InsteonBridgeHandler;
|
||||
@ -41,6 +48,8 @@ public class ModemCommand extends InsteonCommand {
|
||||
private static final String LIST_ALL = "listAll";
|
||||
private static final String LIST_DATABASE = "listDatabase";
|
||||
private static final String RELOAD_DATABASE = "reloadDatabase";
|
||||
private static final String BACKUP_DATABASE = "backupDatabase";
|
||||
private static final String RESTORE_DATABASE = "restoreDatabase";
|
||||
private static final String ADD_DATABASE_CONTROLLER = "addDatabaseController";
|
||||
private static final String ADD_DATABASE_RESPONDER = "addDatabaseResponder";
|
||||
private static final String DELETE_DATABASE_RECORD = "deleteDatabaseRecord";
|
||||
@ -48,16 +57,19 @@ public class ModemCommand extends InsteonCommand {
|
||||
private static final String CLEAR_DATABASE_CHANGES = "clearDatabaseChanges";
|
||||
private static final String ADD_DEVICE = "addDevice";
|
||||
private static final String REMOVE_DEVICE = "removeDevice";
|
||||
private static final String RESET = "reset";
|
||||
private static final String SWITCH = "switch";
|
||||
|
||||
private static final List<String> SUBCMDS = List.of(LIST_ALL, LIST_DATABASE, RELOAD_DATABASE,
|
||||
ADD_DATABASE_CONTROLLER, ADD_DATABASE_RESPONDER, DELETE_DATABASE_RECORD, APPLY_DATABASE_CHANGES,
|
||||
CLEAR_DATABASE_CHANGES, ADD_DEVICE, REMOVE_DEVICE, SWITCH);
|
||||
private static final List<String> SUBCMDS = List.of(LIST_ALL, LIST_DATABASE, RELOAD_DATABASE, BACKUP_DATABASE,
|
||||
RESTORE_DATABASE, ADD_DATABASE_CONTROLLER, ADD_DATABASE_RESPONDER, DELETE_DATABASE_RECORD,
|
||||
APPLY_DATABASE_CHANGES, CLEAR_DATABASE_CHANGES, ADD_DEVICE, REMOVE_DEVICE, RESET, SWITCH);
|
||||
|
||||
private static final String CONFIRM_OPTION = "--confirm";
|
||||
private static final String FORCE_OPTION = "--force";
|
||||
private static final String RECORDS_OPTION = "--records";
|
||||
|
||||
private static final String MODEM_DATABASE_FILE_PREFIX = "modem-database";
|
||||
|
||||
public ModemCommand(InsteonCommandExtension commandExtension) {
|
||||
super(NAME, DESCRIPTION, commandExtension);
|
||||
}
|
||||
@ -69,6 +81,9 @@ public class ModemCommand extends InsteonCommand {
|
||||
buildCommandUsage(LIST_DATABASE + " [" + RECORDS_OPTION + "]",
|
||||
"list all-link database summary or records and pending changes for the Insteon modem"),
|
||||
buildCommandUsage(RELOAD_DATABASE, "reload all-link database from the Insteon modem"),
|
||||
buildCommandUsage(BACKUP_DATABASE, "backup all-link database from the Insteon modem to a file"),
|
||||
buildCommandUsage(RESTORE_DATABASE + " <filename> " + CONFIRM_OPTION,
|
||||
"restore all-link database to the Insteon modem from a specific file"),
|
||||
buildCommandUsage(ADD_DATABASE_CONTROLLER + " <address> <group> [<devCat> <subCat> <firmware>]",
|
||||
"add a controller record to all-link database for the Insteon modem"),
|
||||
buildCommandUsage(ADD_DATABASE_RESPONDER + " <address> <group>",
|
||||
@ -83,6 +98,7 @@ public class ModemCommand extends InsteonCommand {
|
||||
"add an Insteon device to the modem, optionally providing its address"),
|
||||
buildCommandUsage(REMOVE_DEVICE + " <address> [" + FORCE_OPTION + "]",
|
||||
"remove an Insteon device from the modem"),
|
||||
buildCommandUsage(RESET + " " + CONFIRM_OPTION, "reset the Insteon modem to factory defaults"),
|
||||
buildCommandUsage(SWITCH + " <thingId>",
|
||||
"switch Insteon modem bridge to use if more than one configured and enabled"));
|
||||
}
|
||||
@ -118,6 +134,20 @@ public class ModemCommand extends InsteonCommand {
|
||||
printUsage(console, args[0]);
|
||||
}
|
||||
break;
|
||||
case BACKUP_DATABASE:
|
||||
if (args.length == 1) {
|
||||
backupDatabase(console);
|
||||
} else {
|
||||
printUsage(console, args[0]);
|
||||
}
|
||||
break;
|
||||
case RESTORE_DATABASE:
|
||||
if (args.length == 2 || args.length == 3 && CONFIRM_OPTION.equals(args[2])) {
|
||||
restoreDatabase(console, args[1], args.length == 3);
|
||||
} else {
|
||||
printUsage(console, args[0]);
|
||||
}
|
||||
break;
|
||||
case ADD_DATABASE_CONTROLLER:
|
||||
if (args.length == 3 || args.length == 6) {
|
||||
addDatabaseRecord(console, args, true);
|
||||
@ -167,6 +197,13 @@ public class ModemCommand extends InsteonCommand {
|
||||
printUsage(console, args[0]);
|
||||
}
|
||||
break;
|
||||
case RESET:
|
||||
if (args.length == 1 || args.length == 2 && CONFIRM_OPTION.equals(args[1])) {
|
||||
resetModem(console, args.length == 2);
|
||||
} else {
|
||||
printUsage(console, args[0]);
|
||||
}
|
||||
break;
|
||||
case SWITCH:
|
||||
if (args.length == 2) {
|
||||
switchModem(console, args[1]);
|
||||
@ -191,6 +228,10 @@ public class ModemCommand extends InsteonCommand {
|
||||
case LIST_DATABASE:
|
||||
strings = List.of(RECORDS_OPTION);
|
||||
break;
|
||||
case RESTORE_DATABASE:
|
||||
strings = getBindingDataFilePaths(MODEM_DATABASE_FILE_PREFIX).map(Path::getFileName)
|
||||
.map(Path::toString).toList();
|
||||
break;
|
||||
case ADD_DATABASE_CONTROLLER:
|
||||
case ADD_DATABASE_RESPONDER:
|
||||
case REMOVE_DEVICE:
|
||||
@ -200,6 +241,10 @@ public class ModemCommand extends InsteonCommand {
|
||||
strings = getModem().getDB().getRecords().stream().map(record -> record.getAddress().toString())
|
||||
.distinct().toList();
|
||||
break;
|
||||
case APPLY_DATABASE_CHANGES:
|
||||
case RESET:
|
||||
strings = List.of(CONFIRM_OPTION);
|
||||
break;
|
||||
case SWITCH:
|
||||
strings = getBridgeHandlers().map(InsteonBridgeHandler::getThingId).toList();
|
||||
break;
|
||||
@ -207,6 +252,9 @@ public class ModemCommand extends InsteonCommand {
|
||||
} else if (cursorArgumentIndex == 2) {
|
||||
InsteonAddress address = InsteonAddress.isValid(args[1]) ? new InsteonAddress(args[1]) : null;
|
||||
switch (args[0]) {
|
||||
case RESTORE_DATABASE:
|
||||
strings = List.of(CONFIRM_OPTION);
|
||||
break;
|
||||
case DELETE_DATABASE_RECORD:
|
||||
if (address != null) {
|
||||
strings = getModem().getDB().getRecords(address).stream()
|
||||
@ -242,7 +290,8 @@ public class ModemCommand extends InsteonCommand {
|
||||
} else if (entries.isEmpty()) {
|
||||
console.println("The all-link database for modem " + address + " is empty");
|
||||
} else {
|
||||
console.println("The all-link database for modem " + address + " contains " + entries.size() + " devices:");
|
||||
console.println("The all-link database for modem " + address + " contains " + entries.size() + " devices:"
|
||||
+ (!getModem().getDB().isComplete() ? " (Partial)" : ""));
|
||||
print(console, entries);
|
||||
}
|
||||
}
|
||||
@ -255,7 +304,8 @@ public class ModemCommand extends InsteonCommand {
|
||||
} else if (records.isEmpty()) {
|
||||
console.println("The all-link database for modem " + address + " is empty");
|
||||
} else {
|
||||
console.println("The all-link database for modem " + address + " contains " + records.size() + " records:");
|
||||
console.println("The all-link database for modem " + address + " contains " + records.size() + " records:"
|
||||
+ (!getModem().getDB().isComplete() ? " (Partial)" : ""));
|
||||
print(console, records);
|
||||
listDatabaseChanges(console);
|
||||
}
|
||||
@ -263,7 +313,7 @@ public class ModemCommand extends InsteonCommand {
|
||||
|
||||
private void listDatabaseChanges(Console console) {
|
||||
InsteonAddress address = getModem().getAddress();
|
||||
List<String> changes = getModem().getDB().getChanges().stream().map(String::valueOf).toList();
|
||||
List<String> changes = getModem().getDB().getChanges().stream().map(ModemDBChange::toString).toList();
|
||||
if (InsteonAddress.UNKNOWN.equals(address)) {
|
||||
console.println("No modem found!");
|
||||
} else if (!changes.isEmpty()) {
|
||||
@ -285,6 +335,73 @@ public class ModemCommand extends InsteonCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private void backupDatabase(Console console) {
|
||||
InsteonAddress address = getModem().getAddress();
|
||||
if (InsteonAddress.UNKNOWN.equals(address)) {
|
||||
console.println("No modem found!");
|
||||
} else if (!getModem().getDB().isComplete()) {
|
||||
console.println("The all-link database for modem " + address + " is not loaded yet.");
|
||||
} else if (getModem().getDB().getRecords().isEmpty()) {
|
||||
console.println("The all-link database for modem " + address + " is empty");
|
||||
} else {
|
||||
String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date());
|
||||
String id = address.toString().toLowerCase().replace(".", "");
|
||||
Path path = getBindingDataFilePath(MODEM_DATABASE_FILE_PREFIX + "-" + id + "-" + timestamp + ".dmp");
|
||||
byte[] bytes = getModem().getDB().getRecordDump();
|
||||
int count = bytes.length / ModemDBRecord.SIZE;
|
||||
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.write(path, bytes);
|
||||
console.println("Backed up " + count + " database records from modem " + address + " to " + path);
|
||||
} catch (IOException e) {
|
||||
console.println("Failed to write backup file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreDatabase(Console console, String filename, boolean isConfirmed) {
|
||||
InsteonAddress address = getModem().getAddress();
|
||||
if (InsteonAddress.UNKNOWN.equals(address)) {
|
||||
console.println("No modem found!");
|
||||
} else if (!getModem().getDB().isComplete()) {
|
||||
console.println("The all-link database for modem " + address + " is not loaded yet.");
|
||||
} else {
|
||||
Path path = Path.of(filename);
|
||||
if (!path.isAbsolute()) {
|
||||
path = getBindingDataFilePath(filename);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!Files.isReadable(path)) {
|
||||
console.println("The restore file " + path + " does not exist or is not readable.");
|
||||
} else if (Files.size(path) == 0) {
|
||||
console.println("The restore file " + path + " is empty.");
|
||||
} else {
|
||||
InputStream stream = Files.newInputStream(path);
|
||||
List<ModemDBRecord> records = ModemDBRecord.fromRecordDump(stream);
|
||||
if (!isConfirmed) {
|
||||
console.println(
|
||||
"The restore file " + path + " contains " + records.size() + " database records:");
|
||||
print(console, records.stream().map(ModemDBRecord::toString).toList());
|
||||
console.println("Please run the same command with " + CONFIRM_OPTION
|
||||
+ " option to have these database records restored to modem " + address + ".");
|
||||
} else {
|
||||
console.println("Restoring " + records.size() + " database records to modem " + address
|
||||
+ " from " + path + "...");
|
||||
records.forEach(record -> getModem().getDB().markRecordForAddOrModify(record.getAddress(),
|
||||
record.getGroup(), record.isController(), record.getData()));
|
||||
getModem().getDB().update();
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
console.println("The restore file " + path + " is invalid.");
|
||||
} catch (IOException e) {
|
||||
console.println("Failed to read restore file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addDatabaseRecord(Console console, String[] args, boolean isController) {
|
||||
if (!getModem().getDB().isComplete()) {
|
||||
console.println("The modem database is not loaded yet.");
|
||||
@ -313,7 +430,6 @@ public class ModemCommand extends InsteonCommand {
|
||||
ModemDBRecord record = getModem().getDB().getRecord(address, group, isController);
|
||||
if (record == null) {
|
||||
getModem().getDB().markRecordForAdd(address, group, isController, data);
|
||||
|
||||
} else {
|
||||
getModem().getDB().markRecordForModify(record, data);
|
||||
}
|
||||
@ -405,6 +521,19 @@ public class ModemCommand extends InsteonCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private void resetModem(Console console, boolean isConfirmed) {
|
||||
InsteonAddress address = getModem().getAddress();
|
||||
if (InsteonAddress.UNKNOWN.equals(address)) {
|
||||
console.println("No modem found!");
|
||||
} else if (!isConfirmed) {
|
||||
console.println("Please run the same command with " + CONFIRM_OPTION + " option to reset modem " + address
|
||||
+ " to factory defaults.");
|
||||
} else {
|
||||
console.println("Resetting modem " + address + " to factory defaults...");
|
||||
getModem().reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void switchModem(Console console, String thingId) {
|
||||
InsteonBridgeHandler handler = getBridgeHandler(thingId);
|
||||
if (handler == null) {
|
||||
|
@ -153,7 +153,6 @@ public class SceneCommand extends InsteonCommand {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (cursorArgumentIndex == 4) {
|
||||
InsteonDevice device = getInsteonDevice(args[2]);
|
||||
DeviceFeature feature = device != null ? device.getFeature(args[3]) : null;
|
||||
|
@ -980,7 +980,7 @@ public class InsteonDevice extends BaseDevice<InsteonAddress, InsteonDeviceHandl
|
||||
device.setFlags(deviceType.getFlags());
|
||||
}
|
||||
int location = productData.getFirstRecordLocation();
|
||||
if (location != LinkDBRecord.LOCATION_ZERO) {
|
||||
if (location != 0) {
|
||||
device.getLinkDB().setFirstRecordLocation(location);
|
||||
}
|
||||
device.setProductData(productData);
|
||||
|
@ -270,6 +270,26 @@ public class InsteonModem extends BaseDevice<InsteonAddress, InsteonBridgeHandle
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
try {
|
||||
Msg msg = Msg.makeMessage("ResetIM");
|
||||
writeMessage(msg);
|
||||
} catch (IOException e) {
|
||||
logger.warn("error sending modem reset query", e);
|
||||
} catch (InvalidMessageTypeException e) {
|
||||
logger.warn("invalid message ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleModemReset() {
|
||||
logger.debug("modem reset initiated");
|
||||
|
||||
InsteonBridgeHandler handler = getHandler();
|
||||
if (handler != null) {
|
||||
handler.reset(RESET_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
public void logDeviceStatistics() {
|
||||
logger.debug("devices: {} configured, {} polling, msgs received: {}", getDevices().size(),
|
||||
getPollManager().getSizeOfQueue(), msgsReceived);
|
||||
@ -384,18 +404,6 @@ public class InsteonModem extends BaseDevice<InsteonAddress, InsteonBridgeHandle
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that the modem reset process has been initiated
|
||||
*/
|
||||
public void resetInitiated() {
|
||||
logger.debug("modem reset initiated");
|
||||
|
||||
InsteonBridgeHandler handler = getHandler();
|
||||
if (handler != null) {
|
||||
handler.reset(RESET_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that the modem port has disconnected
|
||||
*/
|
||||
@ -462,8 +470,12 @@ public class InsteonModem extends BaseDevice<InsteonAddress, InsteonBridgeHandle
|
||||
}
|
||||
|
||||
private void handleIMMessage(Msg msg) throws FieldException {
|
||||
if (msg.getCommand() == 0x60) {
|
||||
if (msg.getCommand() == 0x60 && msg.isReplyAck()) {
|
||||
// we got an im info reply ack
|
||||
handleModemInfo(msg);
|
||||
} else if (msg.getCommand() == 0x55 || msg.getCommand() == 0x67 && msg.isReplyAck()) {
|
||||
// we got a user reset detected message or im reset reply ack
|
||||
handleModemReset();
|
||||
} else {
|
||||
handleMessage(msg);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import org.openhab.binding.insteon.internal.utils.HexUtils;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DatabaseRecord {
|
||||
public static final int LOCATION_ZERO = 0;
|
||||
public static final int SIZE = 8;
|
||||
|
||||
private final int location;
|
||||
private final RecordType type;
|
||||
@ -42,12 +42,12 @@ public class DatabaseRecord {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public DatabaseRecord(RecordType type, int group, InsteonAddress address, byte[] data) {
|
||||
this(0, type, group, address, data);
|
||||
}
|
||||
|
||||
public DatabaseRecord(DatabaseRecord record) {
|
||||
this.location = record.location;
|
||||
this.type = record.type;
|
||||
this.group = record.group;
|
||||
this.address = record.address;
|
||||
this.data = record.data;
|
||||
this(record.location, record.type, record.group, record.address, record.data);
|
||||
}
|
||||
|
||||
public int getLocation() {
|
||||
@ -114,7 +114,7 @@ public class DatabaseRecord {
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = "";
|
||||
if (location != LOCATION_ZERO) {
|
||||
if (location != 0) {
|
||||
s += HexUtils.getHexString(location, 4) + " ";
|
||||
}
|
||||
s += address + " " + type;
|
||||
|
@ -36,8 +36,6 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LinkDB {
|
||||
public static final int RECORD_BYTE_SIZE = 8;
|
||||
|
||||
private static enum DatabaseStatus {
|
||||
EMPTY,
|
||||
COMPLETE,
|
||||
@ -258,7 +256,7 @@ public class LinkDB {
|
||||
*/
|
||||
public int getNextAvailableLocation() {
|
||||
return getRecords().stream().filter(LinkDBRecord::isAvailable).map(LinkDBRecord::getLocation).findFirst()
|
||||
.orElse(Math.min(getLastRecordLocation(), getLastChangeLocation() - RECORD_BYTE_SIZE));
|
||||
.orElse(Math.min(getLastRecordLocation(), getLastChangeLocation() - LinkDBRecord.SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -355,7 +353,7 @@ public class LinkDB {
|
||||
LinkDBRecord prevRecord = records.put(record.getLocation(), record);
|
||||
// move last record if overwritten
|
||||
if (prevRecord != null && prevRecord.isLast()) {
|
||||
int location = prevRecord.getLocation() - RECORD_BYTE_SIZE;
|
||||
int location = prevRecord.getLocation() - LinkDBRecord.SIZE;
|
||||
records.put(location, LinkDBRecord.withNewLocation(location, prevRecord));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("moved last record for {} to location {}", device.getAddress(),
|
||||
@ -531,7 +529,7 @@ public class LinkDB {
|
||||
|
||||
int firstLocation = records.firstKey();
|
||||
int lastLocation = records.lastKey();
|
||||
int expected = (firstLocation - lastLocation) / RECORD_BYTE_SIZE + 1;
|
||||
int expected = (firstLocation - lastLocation) / LinkDBRecord.SIZE + 1;
|
||||
if (firstLocation != getFirstRecordLocation()) {
|
||||
logger.debug("got unexpected first record location for {}", device.getAddress());
|
||||
setStatus(DatabaseStatus.PARTIAL);
|
||||
|
@ -229,12 +229,12 @@ public class LinkDBReader implements PortListener {
|
||||
stream.write(b);
|
||||
// get next peek byte if stream size below the record byte size
|
||||
// otherwise add record and get next peek record if not done
|
||||
if (stream.size() < LinkDB.RECORD_BYTE_SIZE) {
|
||||
if (stream.size() < LinkDBRecord.SIZE) {
|
||||
getNextPeekByte();
|
||||
} else {
|
||||
addRecord(LinkDBRecord.fromRecordData(stream.toByteArray(), location));
|
||||
if (!done) {
|
||||
location -= LinkDB.RECORD_BYTE_SIZE;
|
||||
location -= LinkDBRecord.SIZE;
|
||||
getNextPeekRecord();
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ public class LinkDBWriter implements PortListener {
|
||||
private void setNextAllLinkRecord() {
|
||||
LinkDBChange change = device.getLinkDB().pollNextChange();
|
||||
if (change == null) {
|
||||
logger.trace("all link db changes written using standard mode for {}", device.getAddress());
|
||||
logger.debug("all link db changes written using standard mode for {}", device.getAddress());
|
||||
done();
|
||||
} else {
|
||||
setAllLinkRecord(change.getRecord());
|
||||
@ -123,7 +123,7 @@ public class LinkDBWriter implements PortListener {
|
||||
private void setNextPokeRecord() {
|
||||
LinkDBChange change = device.getLinkDB().pollNextChange();
|
||||
if (change == null) {
|
||||
logger.trace("all link db changes written using peek/poke mode for {}", device.getAddress());
|
||||
logger.debug("all link db changes written using peek/poke mode for {}", device.getAddress());
|
||||
done();
|
||||
} else {
|
||||
setPokeRecord(change.getRecord());
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.insteon.internal.device.database;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -83,6 +84,14 @@ public class ModemDB {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getRecordDump() {
|
||||
return getRecords().stream().distinct().map(ModemDBRecord::getBytes)
|
||||
.flatMapToInt(bytes -> IntStream.range(0, bytes.length).map(i -> bytes[i]))
|
||||
.collect(ByteArrayOutputStream::new, ByteArrayOutputStream::write,
|
||||
(out1, out2) -> out1.write(out2.toByteArray(), 0, out2.size()))
|
||||
.toByteArray();
|
||||
}
|
||||
|
||||
private Stream<ModemDBRecord> getRecords(@Nullable InsteonAddress address, @Nullable Integer group,
|
||||
@Nullable Boolean isController) {
|
||||
return getRecords().stream()
|
||||
|
@ -175,9 +175,6 @@ public class ModemDBReader implements PortListener {
|
||||
} else if (msg.getCommand() == 0x53) {
|
||||
// we got a linking completed message
|
||||
handleLinkingCompleted(msg);
|
||||
} else if (msg.getCommand() == 0x55 || msg.getCommand() == 0x67 && msg.isReplyAck()) {
|
||||
// we got a user reset detected message or im reset reply ack
|
||||
handleIMReset();
|
||||
} else if (msg.getCommand() == 0x57) {
|
||||
// we got a link record response
|
||||
handleLinkRecord(msg);
|
||||
@ -305,8 +302,4 @@ public class ModemDBReader implements PortListener {
|
||||
productQueries.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIMReset() {
|
||||
modem.resetInitiated();
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,11 @@
|
||||
*/
|
||||
package org.openhab.binding.insteon.internal.device.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.insteon.internal.device.InsteonAddress;
|
||||
import org.openhab.binding.insteon.internal.transport.message.FieldException;
|
||||
@ -26,7 +31,7 @@ import org.openhab.binding.insteon.internal.transport.message.Msg;
|
||||
public class ModemDBRecord extends DatabaseRecord {
|
||||
|
||||
public ModemDBRecord(RecordType type, int group, InsteonAddress address, byte[] data) {
|
||||
super(LOCATION_ZERO, type, group, address, data);
|
||||
super(type, group, address, data);
|
||||
}
|
||||
|
||||
public ModemDBRecord(DatabaseRecord record) {
|
||||
@ -99,6 +104,34 @@ public class ModemDBRecord extends DatabaseRecord {
|
||||
return new ModemDBRecord(type, group, address, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating a list of ModemDBRecord from an Insteon record dump
|
||||
*
|
||||
* @param stream the Insteon record dump input stream to use
|
||||
* @return the list of modem db records
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<ModemDBRecord> fromRecordDump(InputStream stream) throws IllegalArgumentException, IOException {
|
||||
List<ModemDBRecord> records = new ArrayList<>();
|
||||
|
||||
if (stream.available() % ModemDBRecord.SIZE != 0) {
|
||||
throw new IllegalArgumentException("Invalid record dump length");
|
||||
}
|
||||
|
||||
while (stream.available() > 0) {
|
||||
byte[] buf = stream.readNBytes(ModemDBRecord.SIZE);
|
||||
RecordType type = new RecordType(Byte.toUnsignedInt(buf[0]));
|
||||
int group = Byte.toUnsignedInt(buf[1]);
|
||||
InsteonAddress address = new InsteonAddress(buf[2], buf[3], buf[4]);
|
||||
byte[] data = new byte[] { buf[5], buf[6], buf[7] };
|
||||
|
||||
records.add(new ModemDBRecord(type, group, address, data));
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating a new ModemDBRecord from another instance with new data
|
||||
*
|
||||
|
@ -94,7 +94,7 @@ public class ModemDBWriter implements PortListener {
|
||||
private void manageNextModemLinkRecord() {
|
||||
ModemDBChange change = modem.getDB().pollNextChange();
|
||||
if (change == null) {
|
||||
logger.trace("all modem database changes written");
|
||||
logger.debug("all modem database changes written");
|
||||
done();
|
||||
} else {
|
||||
ModemDBRecord record = change.getRecord();
|
||||
|
Loading…
Reference in New Issue
Block a user