Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
64 lines
2.4 KiB
Python
64 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: MPL-2.0
|
|
"""Publish read-only hardware state. No SSIDs, IP addresses or modem identifiers."""
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import time
|
|
|
|
|
|
def battery_state(root=Path('/sys/class/power_supply/battery')):
|
|
try:
|
|
level = int((root/'capacity').read_text().strip())
|
|
status = (root/'status').read_text().strip()
|
|
if not 0 <= level <= 100 or status not in ('Charging', 'Discharging', 'Full', 'Not charging'):
|
|
return None
|
|
return {'level': level/100, 'charging': status in ('Charging', 'Full'), 'status': status}
|
|
except (OSError, ValueError):
|
|
return None
|
|
|
|
|
|
def wifi_state():
|
|
try:
|
|
result = subprocess.run(['nmcli', '-g', 'GENERAL.STATE', 'device', 'show', 'wlan0'],
|
|
capture_output=True, text=True, timeout=3,
|
|
env={**os.environ, 'LC_ALL': 'C'})
|
|
if result.returncode:
|
|
return {'available': False, 'state': 'unavailable'}
|
|
state = int(result.stdout.split()[0])
|
|
names = {10:'unmanaged', 20:'unavailable', 30:'disconnected', 40:'connecting',
|
|
50:'connecting', 60:'authentication', 70:'connecting', 80:'connecting',
|
|
90:'connecting', 100:'connected', 110:'disconnecting', 120:'failed'}
|
|
return {'available': state >= 30, 'state': names.get(state, 'unknown')}
|
|
except (OSError, ValueError, IndexError, subprocess.TimeoutExpired):
|
|
return {'available': False, 'state': 'unavailable'}
|
|
|
|
|
|
def snapshot():
|
|
# Do not D-Bus-activate NetworkManager from an otherwise display-only boot.
|
|
wifi = wifi_state() if Path('/etc/tundra/network-enabled').is_file() else {
|
|
'available': False, 'state': 'unavailable'}
|
|
return {'schema': 1, 'recordedAt': time.time(), 'battery': battery_state(), 'wifi': wifi}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--once', action='store_true')
|
|
args = parser.parse_args()
|
|
if args.once:
|
|
print(json.dumps(snapshot()))
|
|
return
|
|
output = Path('/run/tundra-hardware/status.json')
|
|
output.parent.mkdir(mode=0o755, exist_ok=True)
|
|
while True:
|
|
temporary = output.with_suffix('.tmp')
|
|
temporary.write_text(json.dumps(snapshot())+'\n')
|
|
temporary.chmod(0o644)
|
|
temporary.replace(output)
|
|
time.sleep(5)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|