From 9fc59b2291aa9fdcbe0242c8486dcd8c92a744bb Mon Sep 17 00:00:00 2001 From: jeremysherriff Date: Sat, 2 May 2026 08:32:31 +1200 Subject: [PATCH 1/6] Enhance required_restart.py to use native commands Addresses #239 by detecting available commands, falling back to original behaviour if required. Aligns with sys_updates.py for 6-minute check interval. --- lnxlink/modules/required_restart.py | 58 +++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/lnxlink/modules/required_restart.py b/lnxlink/modules/required_restart.py index 61d4ded..a93fc67 100644 --- a/lnxlink/modules/required_restart.py +++ b/lnxlink/modules/required_restart.py @@ -1,5 +1,10 @@ """Detect if a system reboot is needed""" import os +import time +from shutil import which +from lnxlink.modules.scripts.helpers import syscommand +import logging +logger = logging.getLogger("lnxlink") class Addon: @@ -8,6 +13,34 @@ class Addon: def __init__(self, lnxlink): """Setup addon""" self.name = "Required Restart" + self.last_time = 0 + self.update_interval = 360 # Check for updates every 6 minutes + self.value = { + "needs_restart": "OFF", + "attributes": { + "details": "", + }, + } + + self.restart_checker = None + if which("needrestart") is not None: + self.restart_checker = { + "command": "needrestart -p", + } + elif which("dnf") is not None: + self.restart_checker = { + "command": "dnf needs-restarting -r", + } + elif which("yum") is not None: + self.restart_checker = { + "command": "yum needs-restarting -r", + } + else: + logger.warning("No Required_Restart command detected, consider installing package 'needrestart'.") + logger.warning("Falling back to checking /var/run/reboot-required") + + if self.restart_checker is not None: + logger.info("Required_Restart command found, using '%s'",self.restart_checker["command"]) def exposed_controls(self): """Exposes to home assistant""" @@ -16,11 +49,30 @@ def exposed_controls(self): "type": "binary_sensor", "icon": "mdi:alert-octagon-outline", "entity_category": "diagnostic", + "value_template": "{{ value_json.needs_restart }}", + "attributes_template": "{{ value_json.attributes | tojson }}", }, } def get_info(self): """Gather information from the system""" - if os.path.exists("/var/run/reboot-required"): - return "ON" - return "OFF" + cur_time = time.time() + if cur_time - self.last_time > self.update_interval: + self.last_time = cur_time + + self.value["needs_restart"] = "OFF" + self.value["attributes"]["details"] = "" + if self.restart_checker is not None: + stdout, stderr, returncode = syscommand(self.restart_checker["command"], ignore_errors=True, timeout=30) + if returncode != 0: + self.value["needs_restart"] = "ON" + self.value["attributes"]["details"] = stdout + if stderr: + logger.warning("Required_restart command stderr: %s", stderr) + else: + if os.path.exists("/var/run/reboot-required"): + self.value["needs_restart"] = "ON" + if os.path.exists("/var/run/reboot-required.pkgs"): + with open("/var/run/reboot-required.pkgs", "r") as f: + self.value["attributes"]["details"] = f.read().strip() + return self.value From 29450f1e3ac8b489aaa801147081339444ecdb28 Mon Sep 17 00:00:00 2001 From: jeremysherriff Date: Sat, 2 May 2026 08:49:51 +1200 Subject: [PATCH 2/6] Fix linting --- lnxlink/modules/required_restart.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lnxlink/modules/required_restart.py b/lnxlink/modules/required_restart.py index a93fc67..b508d18 100644 --- a/lnxlink/modules/required_restart.py +++ b/lnxlink/modules/required_restart.py @@ -36,11 +36,16 @@ def __init__(self, lnxlink): "command": "yum needs-restarting -r", } else: - logger.warning("No Required_Restart command detected, consider installing package 'needrestart'.") + logger.warning( + "No Required_Restart command detected, consider installing package 'needrestart'." + ) logger.warning("Falling back to checking /var/run/reboot-required") if self.restart_checker is not None: - logger.info("Required_Restart command found, using '%s'",self.restart_checker["command"]) + logger.info( + "Required_Restart command found, using '%s'", + self.restart_checker["command"] + ) def exposed_controls(self): """Exposes to home assistant""" @@ -63,7 +68,11 @@ def get_info(self): self.value["needs_restart"] = "OFF" self.value["attributes"]["details"] = "" if self.restart_checker is not None: - stdout, stderr, returncode = syscommand(self.restart_checker["command"], ignore_errors=True, timeout=30) + stdout, stderr, returncode = syscommand( + self.restart_checker["command"], + ignore_errors=True, + timeout=30 + ) if returncode != 0: self.value["needs_restart"] = "ON" self.value["attributes"]["details"] = stdout From 65a0ce43bd7b28eec790054198cb66550596f0fb Mon Sep 17 00:00:00 2001 From: jeremysherriff Date: Sat, 2 May 2026 08:57:42 +1200 Subject: [PATCH 3/6] More linting fixes --- lnxlink/modules/required_restart.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lnxlink/modules/required_restart.py b/lnxlink/modules/required_restart.py index b508d18..ed56bea 100644 --- a/lnxlink/modules/required_restart.py +++ b/lnxlink/modules/required_restart.py @@ -44,7 +44,7 @@ def __init__(self, lnxlink): if self.restart_checker is not None: logger.info( "Required_Restart command found, using '%s'", - self.restart_checker["command"] + self.restart_checker["command"], ) def exposed_controls(self): @@ -69,9 +69,7 @@ def get_info(self): self.value["attributes"]["details"] = "" if self.restart_checker is not None: stdout, stderr, returncode = syscommand( - self.restart_checker["command"], - ignore_errors=True, - timeout=30 + self.restart_checker["command"], ignore_errors=True, timeout=30 ) if returncode != 0: self.value["needs_restart"] = "ON" From 8215eba1a80dd9ea16ba417a74a39eb5c2c7bbc4 Mon Sep 17 00:00:00 2001 From: jeremysherriff Date: Sat, 2 May 2026 09:00:28 +1200 Subject: [PATCH 4/6] More linting --- lnxlink/modules/required_restart.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnxlink/modules/required_restart.py b/lnxlink/modules/required_restart.py index ed56bea..3cedfc1 100644 --- a/lnxlink/modules/required_restart.py +++ b/lnxlink/modules/required_restart.py @@ -1,9 +1,10 @@ """Detect if a system reboot is needed""" +import logging import os import time from shutil import which from lnxlink.modules.scripts.helpers import syscommand -import logging + logger = logging.getLogger("lnxlink") From 33ffe8dd97a266fbb6126818a2d1fff47c9603e7 Mon Sep 17 00:00:00 2001 From: jeremysherriff Date: Sat, 2 May 2026 09:11:34 +1200 Subject: [PATCH 5/6] Specify encoding for open() --- lnxlink/modules/required_restart.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lnxlink/modules/required_restart.py b/lnxlink/modules/required_restart.py index 3cedfc1..d46548f 100644 --- a/lnxlink/modules/required_restart.py +++ b/lnxlink/modules/required_restart.py @@ -81,6 +81,8 @@ def get_info(self): if os.path.exists("/var/run/reboot-required"): self.value["needs_restart"] = "ON" if os.path.exists("/var/run/reboot-required.pkgs"): - with open("/var/run/reboot-required.pkgs", "r") as f: - self.value["attributes"]["details"] = f.read().strip() + with open( + "/var/run/reboot-required.pkgs", "r", encoding="utf-8" + ) as pkgs_file: + self.value["attributes"]["details"] = pkgs_file.read().strip() return self.value From ebe9ee4cb7a07a9750ff4355f9fb7f56ecd98c0f Mon Sep 17 00:00:00 2001 From: jeremysherriff Date: Sat, 2 May 2026 09:21:17 +1200 Subject: [PATCH 6/6] Even more linting --- lnxlink/modules/required_restart.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lnxlink/modules/required_restart.py b/lnxlink/modules/required_restart.py index d46548f..80abcf0 100644 --- a/lnxlink/modules/required_restart.py +++ b/lnxlink/modules/required_restart.py @@ -84,5 +84,7 @@ def get_info(self): with open( "/var/run/reboot-required.pkgs", "r", encoding="utf-8" ) as pkgs_file: - self.value["attributes"]["details"] = pkgs_file.read().strip() + self.value["attributes"][ + "details" + ] = pkgs_file.read().strip() return self.value