Add HomieAddonUpgrader to upgradetool (#5180)

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer
2025-12-13 14:48:01 +01:00
committed by GitHub
parent 4d8adbfffe
commit 839581898b
4 changed files with 187 additions and 89 deletions
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2010-2025 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.core.tools;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.storage.json.internal.JsonStorage;
import org.openhab.core.thing.dto.ThingDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ExtractedAddonUpgrader} checks if the parent addon was previously
* installed, and if specific things exist, and if so installs the
* corresponding addon.
*
* @author Cody Cutrer - Initial contribution
*/
@NonNullByDefault
public abstract class ExtractedAddonUpgrader implements Upgrader {
private final Logger logger = LoggerFactory.getLogger(ExtractedAddonUpgrader.class);
public abstract String getParentAddonName();
public abstract String getAddonName();
public abstract boolean thingTypeMatches(String thingTypeUID);
@Override
public boolean execute(@Nullable Path userdataPath, @Nullable Path confPath) {
if (userdataPath == null) {
logger.error("{} skipped: no userdata directory found.", getName());
return false;
}
Path addonsConfigPath = userdataPath.resolve("config/org/openhab/addons.config");
if (!Files.isWritable(addonsConfigPath)) {
logger.error("Cannot access addon config '{}', check path and access rights.", addonsConfigPath);
return false;
}
List<String> configLines;
try {
configLines = Files.readAllLines(addonsConfigPath);
} catch (IOException e) {
logger.error("Failed to read addon config '{}', check path and access rights.", addonsConfigPath);
return false;
}
boolean changed = false;
for (int i = 0; i < configLines.size(); i++) {
String line = Objects.requireNonNull(configLines.get(i));
if (line.contains("," + getAddonName())) {
logger.info("{} addon already installed, skipping installation.", getAddonName());
return true;
}
if (line.contains("," + getParentAddonName())) {
String newLine = line.replace("," + getParentAddonName(),
"," + getParentAddonName() + "," + getAddonName());
configLines.set(i, newLine);
changed = true;
break;
}
}
if (!changed) {
logger.info("{} addon not installed, skipping {} addon installation.", getParentAddonName(),
getAddonName());
return true;
}
Path thingJsonDatabasePath = userdataPath.resolve("jsondb/org.openhab.core.thing.Thing.json");
if (!Files.isReadable(thingJsonDatabasePath)) {
logger.error("Cannot access thing database '{}', check path and access rights.", thingJsonDatabasePath);
return false;
}
JsonStorage<ThingDTO> thingStorage = new JsonStorage<>(thingJsonDatabasePath.toFile(), null, 5, 0, 0,
List.of());
boolean hasHAThing = false;
for (String thingId : thingStorage.getKeys()) {
ThingDTO thing = thingStorage.get(thingId);
if (thing != null && thing.thingTypeUID != null && thingTypeMatches(thing.thingTypeUID.toString())) {
hasHAThing = true;
break;
}
}
if (!hasHAThing) {
logger.info("No {} Things found, skipping addon installation.", getAddonName());
return true;
}
logger.info("Marking {} addon for installation.", getAddonName());
try {
Files.write(addonsConfigPath, configLines);
} catch (IOException e) {
logger.error("Failed to write addon config '{}', check path and access rights.", addonsConfigPath);
return false;
}
return true;
}
}
@@ -55,7 +55,8 @@ public class UpgradeTool {
new JSProfileUpgrader(), // new JSProfileUpgrader(), //
new ScriptProfileUpgrader(), // new ScriptProfileUpgrader(), //
new YamlConfigurationV1TagsUpgrader(), // Added in 5.0 new YamlConfigurationV1TagsUpgrader(), // Added in 5.0
new HomeAssistantAddonUpgrader() // Added in 5.1 new HomeAssistantAddonUpgrader(), // Added in 5.1
new HomieAddonUpgrader() // Added in 5.1
); );
private static final Logger LOGGER = LoggerFactory.getLogger(UpgradeTool.class); private static final Logger LOGGER = LoggerFactory.getLogger(UpgradeTool.class);
@@ -12,19 +12,8 @@
*/ */
package org.openhab.core.tools.internal; package org.openhab.core.tools.internal;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.tools.ExtractedAddonUpgrader;
import org.openhab.core.storage.json.internal.JsonStorage;
import org.openhab.core.thing.dto.ThingDTO;
import org.openhab.core.tools.Upgrader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* The {@link HomeAssistantAddonUpgrader} checks if the MQTT addon was previously * The {@link HomeAssistantAddonUpgrader} checks if the MQTT addon was previously
@@ -32,12 +21,9 @@ import org.slf4j.LoggerFactory;
* Home Assistant addon. * Home Assistant addon.
* *
* @author Cody Cutrer - Initial contribution * @author Cody Cutrer - Initial contribution
* @author Jimmy Tanagra - Refactored into a separate class
*/ */
@NonNullByDefault @NonNullByDefault
public class HomeAssistantAddonUpgrader implements Upgrader { public class HomeAssistantAddonUpgrader extends ExtractedAddonUpgrader {
private final Logger logger = LoggerFactory.getLogger(HomeAssistantAddonUpgrader.class);
@Override @Override
public String getName() { public String getName() {
return "homeAssistantAddonUpgrader"; return "homeAssistantAddonUpgrader";
@@ -49,79 +35,17 @@ public class HomeAssistantAddonUpgrader implements Upgrader {
} }
@Override @Override
public boolean execute(@Nullable Path userdataPath, @Nullable Path confPath) { public String getParentAddonName() {
if (userdataPath == null) { return "mqtt";
logger.error("{} skipped: no userdata directory found.", getName()); }
return false;
}
Path addonsConfigPath = userdataPath.resolve("config/org/openhab/addons.config"); @Override
if (!Files.isWritable(addonsConfigPath)) { public String getAddonName() {
logger.error("Cannot access addon config '{}', check path and access rights.", addonsConfigPath); return "homeassistant";
return false; }
}
List<String> configLines; @Override
try { public boolean thingTypeMatches(String thingTypeUID) {
configLines = Files.readAllLines(addonsConfigPath); return thingTypeUID.startsWith("mqtt:homeassistant");
} catch (IOException e) {
logger.error("Failed to read addon config '{}', check path and access rights.", addonsConfigPath);
return false;
}
boolean changed = false;
for (int i = 0; i < configLines.size(); i++) {
String line = Objects.requireNonNull(configLines.get(i));
if (line.contains(",homeassistant")) {
logger.info("Home Assistant addon already installed, skipping installation.");
return true;
}
if (line.contains(",mqtt")) {
String newLine = line.replace(",mqtt", ",mqtt,homeassistant");
configLines.set(i, newLine);
changed = true;
break;
}
}
if (!changed) {
logger.info("MQTT addon not installed, skipping Home Assistant addon installation.");
return true;
}
Path thingJsonDatabasePath = userdataPath.resolve("jsondb/org.openhab.core.thing.Thing.json");
if (!Files.isReadable(thingJsonDatabasePath)) {
logger.error("Cannot access thing database '{}', check path and access rights.", thingJsonDatabasePath);
return false;
}
JsonStorage<ThingDTO> thingStorage = new JsonStorage<>(thingJsonDatabasePath.toFile(), null, 5, 0, 0,
List.of());
boolean hasHAThing = false;
for (String thingId : thingStorage.getKeys()) {
ThingDTO thing = thingStorage.get(thingId);
if (thing != null && thing.thingTypeUID != null
&& thing.thingTypeUID.toString().startsWith("mqtt:homeassistant")) {
hasHAThing = true;
break;
}
}
if (!hasHAThing) {
logger.info("No Home Assistant Things found, skipping addon installation.");
return true;
}
logger.info("Marking Home Assistant addon for installation.");
try {
Files.write(addonsConfigPath, configLines);
} catch (IOException e) {
logger.error("Failed to write addon config '{}', check path and access rights.", addonsConfigPath);
return false;
}
return true;
} }
} }
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2010-2025 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.core.tools.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.tools.ExtractedAddonUpgrader;
/**
* The {@link HomeAssistantAddonUpgrader} checks if the MQTT addon was previously
* installed, and if Home Assistant things exist, and if so installs the
* Home Assistant addon.
*
* @author Cody Cutrer - Initial contribution
*/
@NonNullByDefault
public class HomieAddonUpgrader extends ExtractedAddonUpgrader {
@Override
public String getName() {
return "homieAddonUpgrader";
}
@Override
public String getDescription() {
return "Installs the Homie addon if Homie Things are present";
}
@Override
public String getParentAddonName() {
return "mqtt";
}
@Override
public String getAddonName() {
return "homie";
}
@Override
public boolean thingTypeMatches(String thingTypeUID) {
return thingTypeUID.startsWith("mqtt:homie");
}
}