reboot_RPi.RebootManager

class reboot_RPi.RebootManager

Coordinates Raspberry Pi reboots with cooldown protection.

Persists a record of each successful reboot to LOG_FILE and enforces a minimum interval (COOLDOWN_SECONDS) between consecutive reboots. This prevents cascading reboot loops when a persistent hardware or configuration error triggers repeated reboot requests from upstream components such as the watchdog or sensor error handlers.

Variables:
  • LOG_FILE (str) – Path to the persistent reboot history file. Each line is a pipe-delimited record produced by write_reboot_log().

  • COOLDOWN_SECONDS (int) – Minimum number of seconds that must elapse between two reboots; defaults to 4 hours.

Example

Attempt a reboot for an operator-initiated event:

manager = RebootManager()
manager.reboot_raspberry_pi({
    "type": "events",
    "t": int(time.time() * 1000),
    "msg": {"type": "manual", "value": "ops"},
})  # doctest: +SKIP
read_last_reboot() int | None

Read the timestamp of the most recent reboot from the log file.

Opens LOG_FILE, examines the last line, and extracts the epoch-millisecond value stored in the t=<epoch_ms> field of a record written by write_reboot_log(). Any parsing or I/O error is logged through OizomLogger and reduced to a None return so the caller treats the device as never having rebooted.

Parameters:

None.

Returns:

Epoch timestamp in milliseconds of the last reboot, or None if no previous reboot is recorded, the file is missing, empty, or cannot be parsed.

:raises None. All Exception instances are caught and: :raises converted:

Example

>>> manager = RebootManager()
>>> manager.read_last_reboot()

Note

The function intentionally swallows exceptions so that a corrupt log file cannot block future reboots; integrity of the log is not security-critical.

reboot_raspberry_pi(payload: dict) None

Trigger a SysRq reboot if outside the cooldown window.

Reads the last reboot timestamp via read_last_reboot() and compares it with payload['t']. When the elapsed time is less than COOLDOWN_SECONDS, the reboot is suppressed and a context-tagged informational message is emitted via the module-level OizomLogger. Otherwise the event is appended to LOG_FILE and an immediate Magic SysRq reboot is issued by writing "b" to /proc/sysrq-trigger.

Parameters:

payload

Reboot event dictionary with the structure:

{
    "type": "events",
    "t": <epoch_ms>,
    "msg": {"type": "...", "value": "..."}
}

Returns:

None.

:raises None directly. Any OSError from the write to: :raises /proc/sysrq-trigger` (or the preceding sleep) is caugh: :raises and logged; the process then returns` to :py:class:`the caller instead: :raises of propagating the failure.:

Example

>>> manager = RebootManager()
>>> manager.reboot_raspberry_pi({
...     "type": "events",
...     "t": 1700000000000,
...     "msg": {"type": "manual", "value": "ops"},
... })

Note

Requires root privileges (or CAP_SYS_ADMIN) because writes to /proc/sysrq-trigger are restricted; a kernel with CONFIG_MAGIC_SYSRQ is also required.

write_reboot_log(payload: dict) bool

Append a reboot event record to LOG_FILE.

Serializes payload into a single pipe-delimited line of the form t=<epoch_ms> | type=<...> | msg_type=<...> | msg_value=<...>, appends it to the log file, flushes the userspace buffer, and attempts os.fsync() to make the write durable before the impending hardware reboot. A short sleep keeps the kernel a moment to commit the write on slow storage.

Parameters:

payload

Reboot event dictionary with the following keys:

  • "t" (int): epoch timestamp in milliseconds.

  • "type" (str): event category, e.g. "events".

  • "msg" (dict): nested dictionary with "type" and "value" string fields describing the cause.

Returns:

True after successfully writing and flushing the entry.

Raises:
  • KeyError – If payload is missing any required key.

  • OSError – If the log file cannot be opened for append.

Example

>>> manager = RebootManager()
>>> manager.write_reboot_log({
...     "t": 1700000000000,
...     "type": "events",
...     "msg": {"type": "manual", "value": "test"},
... })

Note

os.fsync() may fail on some filesystems (e.g. those backed by /proc); failures are swallowed to keep the reboot path moving.

COOLDOWN_SECONDS = 14400
LOG_FILE = 'reboot_log.txt'