-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[nikohomecontrol] Add console commands (#17352)
* dump devices from console Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
- Loading branch information
Showing
10 changed files
with
361 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
280 changes: 280 additions & 0 deletions
280
...org/openhab/binding/nikohomecontrol/internal/console/NikoHomeControlCommandExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
/** | ||
* Copyright (c) 2010-2024 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.nikohomecontrol.internal.console; | ||
|
||
import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.BINDING_ID; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.nio.file.StandardOpenOption; | ||
import java.time.LocalDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
import org.eclipse.jdt.annotation.Nullable; | ||
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler; | ||
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler2; | ||
import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlDiscover; | ||
import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NikoHomeControlCommunication2; | ||
import org.openhab.core.io.console.Console; | ||
import org.openhab.core.io.console.ConsoleCommandCompleter; | ||
import org.openhab.core.io.console.StringsCompleter; | ||
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension; | ||
import org.openhab.core.io.console.extensions.ConsoleCommandExtension; | ||
import org.openhab.core.net.NetworkAddressService; | ||
import org.openhab.core.thing.ThingRegistry; | ||
import org.openhab.core.thing.ThingStatus; | ||
import org.osgi.service.component.annotations.Activate; | ||
import org.osgi.service.component.annotations.Component; | ||
import org.osgi.service.component.annotations.Reference; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.GsonBuilder; | ||
import com.google.gson.JsonArray; | ||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonParser; | ||
import com.google.gson.JsonSyntaxException; | ||
|
||
/** | ||
* The {@link NikoHomeControlCommandExtension} is responsible for handling console commands | ||
* | ||
* @author Mark Herwege - Initial contribution | ||
*/ | ||
|
||
@NonNullByDefault | ||
@Component(service = ConsoleCommandExtension.class) | ||
public class NikoHomeControlCommandExtension extends AbstractConsoleCommandExtension | ||
implements ConsoleCommandCompleter { | ||
|
||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); | ||
|
||
private static final String CONTROLLERS = "controllers"; | ||
private static final String SYSTEMINFO = "systeminfo"; | ||
private static final String DEVICELIST = "devicelist"; | ||
private static final String DUMP = "dump"; | ||
|
||
private static final String ROOT_PATH = System.getProperty("user.home") + File.separator + BINDING_ID | ||
+ File.separator + DEVICELIST; | ||
|
||
private static final StringsCompleter CMD_COMPLETER = new StringsCompleter( | ||
List.of(CONTROLLERS, SYSTEMINFO, DEVICELIST), false); | ||
private static final StringsCompleter DUMP_COMPLETER = new StringsCompleter(List.of(DUMP), false); | ||
|
||
private final ThingRegistry thingRegistry; | ||
private final NetworkAddressService networkAddressService; | ||
|
||
private List<NikoHomeControlBridgeHandler> bridgeHandlers = List.of(); | ||
|
||
@Activate | ||
public NikoHomeControlCommandExtension(final @Reference ThingRegistry thingRegistry, | ||
final @Reference NetworkAddressService networkAddressService) { | ||
super("nikohomecontrol", "Interact with the Niko Home Control binding"); | ||
this.thingRegistry = thingRegistry; | ||
this.networkAddressService = networkAddressService; | ||
} | ||
|
||
@Override | ||
public void execute(String[] args, Console console) { | ||
if ((args.length < 1) || (args.length > 3)) { | ||
console.println("Invalid number of arguments"); | ||
printUsage(console); | ||
return; | ||
} | ||
|
||
bridgeHandlers = thingRegistry.getAll().stream() | ||
.filter(t -> t.getHandler() instanceof NikoHomeControlBridgeHandler) | ||
.map(b -> ((NikoHomeControlBridgeHandler) b.getHandler())).toList(); | ||
Map<String, String> bridgeNhcVersion = bridgeHandlers.stream().collect(Collectors | ||
.toMap(b -> b.getControllerId(), b -> b instanceof NikoHomeControlBridgeHandler2 ? "II" : "I")); | ||
|
||
NikoHomeControlBridgeHandler bridgeHandler = null; | ||
if (args.length > 1) { | ||
Optional<NikoHomeControlBridgeHandler> bridgeOptional = bridgeHandlers.stream() | ||
.filter(b -> b.getControllerId().toLowerCase().equals(args[1].toLowerCase())).findAny(); | ||
if (bridgeOptional.isEmpty()) { | ||
console.println("'" + args[1] + "' is not a valid controller ID"); | ||
printUsage(console); | ||
return; | ||
} | ||
bridgeHandler = bridgeOptional.get(); | ||
} | ||
|
||
switch (args[0].toLowerCase()) { | ||
case CONTROLLERS: | ||
if (args.length > 1) { | ||
console.println("No extra argument allowed after 'controllers'"); | ||
printUsage(console); | ||
return; | ||
} else { | ||
Map<String, String> bridgeIds = bridgeHandlers.stream().collect(Collectors | ||
.toMap(b -> b.getThing().getUID().toString(), b -> b.getControllerId().toLowerCase())); | ||
List<String> controllerIds = List.of(); | ||
Map<String, String> controllerNhcVersion = Map.of(); | ||
try { | ||
String broadcastAddr = networkAddressService.getConfiguredBroadcastAddress(); | ||
if (broadcastAddr == null) { | ||
console.println( | ||
"Controller discovery not possible, no broadcast address found, result only contains bridges"); | ||
} else { | ||
NikoHomeControlDiscover nhcDiscover; | ||
nhcDiscover = new NikoHomeControlDiscover(broadcastAddr); | ||
controllerIds = nhcDiscover.getNhcBridgeIds().stream().map(String::toLowerCase) | ||
.filter(id -> !bridgeIds.containsValue(id)).toList(); | ||
controllerNhcVersion = controllerIds.stream().collect( | ||
Collectors.toMap(Function.identity(), id -> nhcDiscover.isNhcII(id) ? "II" : "I")); | ||
} | ||
} catch (IOException e) { | ||
console.println( | ||
"Controller discovery not possible, network error, result only contains bridges"); | ||
} | ||
Map<String, String> nhcVersion = Map.copyOf(controllerNhcVersion); | ||
console.printf("%-14s %-12s %s%n", "Controller-ID", "NHC-Version", "Bridge-ID"); | ||
console.printf("%-14s %-12s %s%n", "-------------", "-----------", "---------"); | ||
bridgeIds.forEach( | ||
(bridge, id) -> console.printf("%-14s %-12s %s%n", id, bridgeNhcVersion.get(id), bridge)); | ||
controllerIds.forEach(id -> console.printf("%-14s %-12s %s%n", id, nhcVersion.get(id), " ")); | ||
} | ||
break; | ||
case SYSTEMINFO: | ||
if (args.length < 2) { | ||
console.println("No controller ID provided"); | ||
printUsage(console); | ||
return; | ||
} | ||
if (bridgeHandler != null) { | ||
if (!ThingStatus.ONLINE.equals(bridgeHandler.getThing().getStatus())) { | ||
console.println("Niko Home Control bridge not online, system info may be out of date"); | ||
} | ||
console.println("Property Value"); | ||
console.println("-------- -----"); | ||
Map<String, String> properties = bridgeHandler.getThing().getProperties(); | ||
for (String key : properties.keySet()) { | ||
console.printf("%-28.28s %s%n", key, properties.get(key)); | ||
} | ||
} | ||
break; | ||
case DEVICELIST: | ||
if (args.length < 2) { | ||
console.println("No controller ID provided"); | ||
printUsage(console); | ||
return; | ||
} | ||
if (!"II".equals(bridgeNhcVersion.get(args[1]))) { | ||
console.println("'" + args[1] + "' is not a Niko Home Control II bridge"); | ||
printUsage(console); | ||
return; | ||
} | ||
if (bridgeHandler != null) { | ||
if (!ThingStatus.ONLINE.equals(bridgeHandler.getThing().getStatus())) { | ||
console.println("Niko Home Control bridge not online, device list may be out of date"); | ||
} | ||
NikoHomeControlCommunication2 nhcComm = (NikoHomeControlCommunication2) bridgeHandler | ||
.getCommunication(); | ||
if (nhcComm != null) { | ||
String devices = prettyJson(nhcComm.getRawDevicesListResponse()); | ||
if (args.length == 2) { | ||
console.println(devices); | ||
} else if (DUMP.equals(args[2])) { | ||
String filename = ROOT_PATH + LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE) | ||
+ ".json"; | ||
String systeminfo = GSON.toJson(bridgeHandler.getThing().getProperties()); | ||
writeJsonToFile(filename, systeminfo, console); | ||
writeJsonToFile(filename, devices, console); | ||
console.printf("System info and device list dumped to file '%s'%n", filename); | ||
} else { | ||
console.println("Command argument '" + args[2] + "' not recognized"); | ||
printUsage(console); | ||
return; | ||
} | ||
} | ||
} | ||
break; | ||
default: | ||
console.println("Command argument '" + args[0] + "' not recognized"); | ||
printUsage(console); | ||
} | ||
} | ||
|
||
private String prettyJson(String json) { | ||
try { | ||
return GSON.toJson(JsonParser.parseString(json)); | ||
} catch (JsonSyntaxException e) { | ||
// Keep the unformatted json if there is a syntax exception | ||
return json; | ||
} | ||
} | ||
|
||
private void writeJsonToFile(String filename, String json, Console console) { | ||
try { | ||
JsonElement element = JsonParser.parseString(json); | ||
if (element.isJsonNull() || (element.isJsonArray() && ((JsonArray) element).size() == 0)) { | ||
console.println("Empty device list, nothing to dump"); | ||
return; | ||
} | ||
} catch (JsonSyntaxException e) { | ||
// Just continue and write the file with non-valid json anyway | ||
} | ||
|
||
// ensure full path exists | ||
File file = new File(filename); | ||
File parentFile = file.getParentFile(); | ||
if (parentFile != null) { | ||
parentFile.mkdirs(); | ||
} | ||
|
||
final byte[] contents = (json + "\n").getBytes(StandardCharsets.UTF_8); | ||
try { | ||
Files.write(file.toPath(), contents, StandardOpenOption.CREATE, StandardOpenOption.APPEND); | ||
} catch (IOException e) { | ||
console.println("I/O error writing device list to file"); | ||
} | ||
} | ||
|
||
@Override | ||
public List<String> getUsages() { | ||
return Arrays.asList(new String[] { buildCommandUsage(CONTROLLERS, "list all Niko Home Control Controllers"), | ||
buildCommandUsage(SYSTEMINFO + " <controller ID>", | ||
"show system info for Controller with controller ID"), | ||
buildCommandUsage(DEVICELIST + " <controller ID>", | ||
"create device list of installation on Controller with controller ID"), | ||
buildCommandUsage(DEVICELIST + " <controller ID> " + DUMP, | ||
"dump device list of installation with controller ID to file") }); | ||
} | ||
|
||
@Override | ||
public @Nullable ConsoleCommandCompleter getCompleter() { | ||
return this; | ||
} | ||
|
||
@Override | ||
public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) { | ||
if (cursorArgumentIndex <= 0) { | ||
return CMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates); | ||
} else if (cursorArgumentIndex == 1) { | ||
return new StringsCompleter(bridgeHandlers.stream().filter(b -> b instanceof NikoHomeControlBridgeHandler2) | ||
.map(b -> b.getControllerId()).toList(), false) | ||
.complete(args, cursorArgumentIndex, cursorPosition, candidates); | ||
} else if (cursorArgumentIndex == 2) { | ||
return DUMP_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates); | ||
} | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.