<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.geekworm.com/index.php?action=history&amp;feed=atom&amp;title=CommentStreams%3ABe43973df13378ab1a3a45c6e7d5b910</id>
	<title>CommentStreams:Be43973df13378ab1a3a45c6e7d5b910 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.geekworm.com/index.php?action=history&amp;feed=atom&amp;title=CommentStreams%3ABe43973df13378ab1a3a45c6e7d5b910"/>
	<link rel="alternate" type="text/html" href="https://wiki.geekworm.com/index.php?title=CommentStreams:Be43973df13378ab1a3a45c6e7d5b910&amp;action=history"/>
	<updated>2026-07-25T23:42:03Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://wiki.geekworm.com/index.php?title=CommentStreams:Be43973df13378ab1a3a45c6e7d5b910&amp;diff=35058&amp;oldid=prev</id>
		<title>37.201.192.11: Migrated comment #5702</title>
		<link rel="alternate" type="text/html" href="https://wiki.geekworm.com/index.php?title=CommentStreams:Be43973df13378ab1a3a45c6e7d5b910&amp;diff=35058&amp;oldid=prev"/>
		<updated>2025-03-28T04:25:10Z</updated>

		<summary type="html">&lt;p&gt;Migrated comment #5702&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;#!/usr/bin/env python3&lt;br /&gt;
# This script is only suitable for UPS Shield X1200, X1201, and X1202&lt;br /&gt;
&lt;br /&gt;
import struct&lt;br /&gt;
import smbus2&lt;br /&gt;
import time&lt;br /&gt;
import os&lt;br /&gt;
import logging&lt;br /&gt;
import gpiod&lt;br /&gt;
from subprocess import call&lt;br /&gt;
&lt;br /&gt;
# Define the log file locations&lt;br /&gt;
LOG_FILE = &amp;quot;/var/log/ups_battery.log&amp;quot;  # Main log with timestamps&lt;br /&gt;
GLANCES_FILE = &amp;quot;/var/log/ups_battery_glances.log&amp;quot;  # Always contains only the latest log entry&lt;br /&gt;
MAX_LOG_SIZE = 10 * 1024 * 1024  # 10MB maximum log file size&lt;br /&gt;
KEEP_LINES = 500  # Number of lines to retain after truncation&lt;br /&gt;
&lt;br /&gt;
# Define the shutdown trigger file path&lt;br /&gt;
SHUTDOWN_TRIGGER_FILE = &amp;quot;/home/dockeruser/vault_con_data/commands/trigger/shutdown_vaultwarden&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure logging&lt;br /&gt;
logging.basicConfig(&lt;br /&gt;
    filename=LOG_FILE,&lt;br /&gt;
    level=logging.INFO,&lt;br /&gt;
    format=&amp;quot;%(asctime)s - %(levelname)s - %(message)s&amp;quot;,&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
bus = smbus2.SMBus(1)&lt;br /&gt;
&lt;br /&gt;
# **AC Power Detection via GPIO 6 (gpiochip4)**&lt;br /&gt;
PLD_PIN = 6&lt;br /&gt;
chip = gpiod.Chip(&amp;#039;gpiochip4&amp;#039;)  # Use gpiochip4 (as per previous tests)&lt;br /&gt;
pld_line = chip.get_line(PLD_PIN)&lt;br /&gt;
pld_line.request(consumer=&amp;quot;PLD&amp;quot;, type=gpiod.LINE_REQ_DIR_IN)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def truncate_log_file():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Truncate the log file if it exceeds the max size by keeping only the last KEEP_LINES lines.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if os.path.exists(LOG_FILE) and os.path.getsize(LOG_FILE) &amp;gt; MAX_LOG_SIZE:&lt;br /&gt;
        try:&lt;br /&gt;
            with open(LOG_FILE, &amp;quot;r&amp;quot;) as f:&lt;br /&gt;
                lines = f.readlines()[-KEEP_LINES:]  # Keep last X lines&lt;br /&gt;
&lt;br /&gt;
            with open(LOG_FILE, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
                f.writelines(lines)  # Overwrite with retained lines&lt;br /&gt;
&lt;br /&gt;
            logging.info(&amp;quot;🗑️ Log file truncated to last %d lines.&amp;quot;, KEEP_LINES)&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            logging.error(f&amp;quot;Failed to truncate log file: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def log_message(message):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Write a message to both the log file and the terminal.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(message)  # Print to console&lt;br /&gt;
&lt;br /&gt;
    truncate_log_file()  # Ensure log doesn&amp;#039;t exceed the limit before writing&lt;br /&gt;
&lt;br /&gt;
    logging.info(message)  # Write to the main log file&lt;br /&gt;
&lt;br /&gt;
    # Write the latest status to GLANCES_FILE, overwriting previous content&lt;br /&gt;
    try:&lt;br /&gt;
        with open(GLANCES_FILE, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            f.write(message + &amp;quot;\n&amp;quot;)  # Overwrite previous entry with new one&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        logging.error(f&amp;quot;Failed to write to {GLANCES_FILE}: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def readVoltage(bus):&lt;br /&gt;
    address = 0x36&lt;br /&gt;
    read = bus.read_word_data(address, 2)&lt;br /&gt;
    swapped = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, struct.pack(&amp;quot;&amp;gt;H&amp;quot;, read))[0]&lt;br /&gt;
    voltage = swapped * 1.25 / 1000 / 16&lt;br /&gt;
    return voltage&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def readCapacity(bus):&lt;br /&gt;
    address = 0x36&lt;br /&gt;
    read = bus.read_word_data(address, 4)&lt;br /&gt;
    swapped = struct.unpack(&amp;quot;&amp;lt;H&amp;quot;, struct.pack(&amp;quot;&amp;gt;H&amp;quot;, read))[0]&lt;br /&gt;
    capacity = swapped / 256&lt;br /&gt;
    return capacity&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def is_ac_power_connected():&lt;br /&gt;
    return pld_line.get_value() == 1  # 1 = AC Plugged In, 0 = Unplugged&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# **Charging Control via pinctrl (GPIO 16)**&lt;br /&gt;
def enable_charging():&lt;br /&gt;
    os.system(&amp;quot;sudo pinctrl set 16 op dl&amp;quot;)  # Enable charging&lt;br /&gt;
    log_message(&amp;quot;🔋 Charging set to ENABLED.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def disable_charging():&lt;br /&gt;
    os.system(&amp;quot;sudo pinctrl set 16 op dh&amp;quot;)  # Disable charging&lt;br /&gt;
    log_message(&amp;quot;🔋 Charging set to DISABLED.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def try_force_enable_charging():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Try to force enable charging if battery is too low (&amp;lt;40%).&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    log_message(&amp;quot;⚠️ Attempting to FORCE ENABLE charging (Battery &amp;lt;40%)&amp;quot;)&lt;br /&gt;
    enable_charging()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def try_force_disable_charging():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Try to force disable charging if battery is too high (&amp;gt;70%).&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    log_message(&amp;quot;⚠️ Attempting to FORCE DISABLE charging (Battery &amp;gt;70%)&amp;quot;)&lt;br /&gt;
    disable_charging()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# **Track AC Connection &amp;amp; Charging State**&lt;br /&gt;
previous_ac_state = None&lt;br /&gt;
previous_charging_state = None&lt;br /&gt;
&lt;br /&gt;
# **Check Initial Battery Level on Startup**&lt;br /&gt;
voltage = readVoltage(bus)&lt;br /&gt;
capacity = readCapacity(bus)&lt;br /&gt;
ac_power_state = is_ac_power_connected()  # Detect AC power status&lt;br /&gt;
&lt;br /&gt;
log_message(&amp;quot;⚠️ POWER DETECTION STARTED (REBOOT?)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if capacity &amp;gt;= 49:&lt;br /&gt;
    disable_charging()&lt;br /&gt;
    charging_enabled = False&lt;br /&gt;
else:&lt;br /&gt;
    enable_charging()&lt;br /&gt;
    charging_enabled = True&lt;br /&gt;
&lt;br /&gt;
previous_charging_state = charging_enabled  # ✅ Initialize this properly&lt;br /&gt;
&lt;br /&gt;
while True:&lt;br /&gt;
    voltage = readVoltage(bus)&lt;br /&gt;
    capacity = readCapacity(bus)&lt;br /&gt;
    ac_power_state = is_ac_power_connected()  # Detect AC power status&lt;br /&gt;
&lt;br /&gt;
    # Log AC connection status when it changes&lt;br /&gt;
    if ac_power_state != previous_ac_state:&lt;br /&gt;
        log_message(&amp;quot;🔌 AC Power &amp;quot; + (&amp;quot;CONNECTED.&amp;quot; if ac_power_state else &amp;quot;DISCONNECTED!&amp;quot;))&lt;br /&gt;
        previous_ac_state = ac_power_state&lt;br /&gt;
&lt;br /&gt;
    # Forcing if wrong variable were set&lt;br /&gt;
    if capacity &amp;gt; 60:&lt;br /&gt;
        try_force_disable_charging()&lt;br /&gt;
        charging_enabled = False  # Mark charging as disabled&lt;br /&gt;
&lt;br /&gt;
    elif capacity &amp;lt; 40:&lt;br /&gt;
        try_force_enable_charging()&lt;br /&gt;
        charging_enabled = True  # Mark charging as enabled&lt;br /&gt;
&lt;br /&gt;
    else:  # Target Operation&lt;br /&gt;
        if capacity &amp;lt; 45 and not charging_enabled:  # ✅ Properly enables charging at 40-44%&lt;br /&gt;
            enable_charging()&lt;br /&gt;
            charging_enabled = True&lt;br /&gt;
&lt;br /&gt;
        elif capacity &amp;gt;= 49 and charging_enabled:  # ✅ Properly disables charging at 50%&lt;br /&gt;
            disable_charging()&lt;br /&gt;
            charging_enabled = False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    # **Only Log Charging State When It Changes**&lt;br /&gt;
    if charging_enabled != previous_charging_state:&lt;br /&gt;
        log_message(f&amp;quot;⚡ Battery charging state changed: {&amp;#039;ENABLED&amp;#039; if charging_enabled else &amp;#039;DISABLED&amp;#039;}.&amp;quot;)&lt;br /&gt;
        previous_charging_state = charging_enabled  # ✅ Update this every loop&lt;br /&gt;
&lt;br /&gt;
    # **Shutdown only if battery is critically low AND AC power is unplugged**&lt;br /&gt;
    if capacity &amp;lt;= 35 and not ac_power_state:&lt;br /&gt;
        log_message(&amp;quot;🚨 CRITICAL: Battery LOW and AC Power is OFF! System will shut down.&amp;quot;)&lt;br /&gt;
        os.system(f&amp;quot;touch {SHUTDOWN_TRIGGER_FILE}&amp;quot;)&lt;br /&gt;
        log_message(f&amp;quot;📂 Shutdown trigger file created: {SHUTDOWN_TRIGGER_FILE}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        log_message(&amp;quot;⏳ Shutdown in 30 seconds...&amp;quot;)&lt;br /&gt;
        time.sleep(30)  # Wait before shutting down&lt;br /&gt;
&lt;br /&gt;
        log_message(&amp;quot;🛑 System shutting down now.&amp;quot;)&lt;br /&gt;
        call(&amp;quot;sudo nohup shutdown -h now&amp;quot;, shell=True)&lt;br /&gt;
&lt;br /&gt;
    battery_status = f&amp;quot;Voltage: {voltage:.2f}V | Battery: {capacity:.0f}% | AC Power: {&amp;#039;Plugged in&amp;#039; if ac_power_state else &amp;#039;Unplugged&amp;#039;} | Charging: {&amp;#039;Active&amp;#039; if charging_enabled and ac_power_state else &amp;#039;Inactive&amp;#039;}&amp;quot;&lt;br /&gt;
    log_message(battery_status)&lt;br /&gt;
&lt;br /&gt;
    time.sleep(2)&amp;lt;!-- migrated from Comments; original IP: 37.201.192.11 --&amp;gt;&lt;/div&gt;</summary>
		<author><name>37.201.192.11</name></author>
	</entry>
</feed>