frobarng: NetworkProvider done

This commit is contained in:
Geoffrey Frogeye 2024-09-26 23:04:11 +02:00
parent 36df032ecd
commit 1ae7d6b447
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8

View file

@ -3,9 +3,11 @@
import asyncio
import datetime
import enum
import ipaddress
import logging
import random
import signal
import socket
import typing
import coloredlogs
@ -22,6 +24,21 @@ C = typing.TypeVar("C", bound="ComposableText")
Sortable = str | int
def humanSize(numi: int) -> str:
"""
Returns a string of width 3+3
"""
num = float(numi)
for unit in ("B ", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"):
if abs(num) < 1000:
if num >= 10:
return "{:3d}{}".format(int(num), unit)
else:
return "{:.1f}{}".format(num, unit)
num /= 1024
return "{:d}YiB".format(numi)
class ComposableText(typing.Generic[P, C]):
def __init__(
@ -267,6 +284,7 @@ class Bar(ComposableText):
super().__init__()
self.parent: None
self.children: typing.MutableSequence[Screen]
self.longRunningTasks: list[asyncio.Task] = list()
self.refresh = asyncio.Event()
self.taskGroup = asyncio.TaskGroup()
@ -280,6 +298,10 @@ class Bar(ComposableText):
continue
Screen(parent=self, output=output.name)
def addLongRunningTask(self, coro: typing.Coroutine) -> None:
task = self.taskGroup.create_task(coro)
self.longRunningTasks.append(task)
async def run(self) -> None:
cmd = [
"lemonbar",
@ -299,7 +321,6 @@ class Bar(ComposableText):
await self.refresh.wait()
self.refresh.clear()
markup = self.getMarkup()
# log.debug(markup)
proc.stdin.write(markup.encode())
async def actionHandler() -> None:
@ -310,21 +331,15 @@ class Bar(ComposableText):
callback = self.actions[command]
callback()
longRunningTasks = list()
def addLongRunningTask(coro: typing.Coroutine) -> None:
task = self.taskGroup.create_task(coro)
longRunningTasks.append(task)
async with self.taskGroup:
addLongRunningTask(refresher())
addLongRunningTask(actionHandler())
self.addLongRunningTask(refresher())
self.addLongRunningTask(actionHandler())
for provider in self.providers:
addLongRunningTask(provider.run())
self.addLongRunningTask(provider.run())
def exit() -> None:
log.info("Terminating")
for task in longRunningTasks:
for task in self.longRunningTasks:
task.cancel()
loop = asyncio.get_event_loop()
@ -563,11 +578,50 @@ class NetworkProviderSection(StatefulSection):
self.numberStates = 5 if self.wifi else 4
self.state = 1 if self.wifi else 0
async def updateOnStateChange(self) -> None:
while True:
await self.stateChanged.wait()
await self.provider.updateIface(self.iface)
async def getText(self) -> str | None:
if self.ignore or not self.provider.if_stats[self.iface].isup:
return None
text = self.icon
state = self.state + (0 if self.wifi else 1) # SSID
if self.wifi and state >= 1:
cmd = ["iwgetid", self.iface, "--raw"]
proc = await asyncio.create_subprocess_exec(
*cmd, stdout=asyncio.subprocess.PIPE
)
stdout, stderr = await proc.communicate()
text += f" {stdout.decode().strip()}"
if state >= 2: # Address
for address in self.provider.if_addrs[self.iface]:
if address.family == socket.AF_INET:
net = ipaddress.IPv4Network(
(address.address, address.netmask), strict=False
)
text += f" {net.with_prefixlen}"
break
if state >= 3: # Speed
prevRecv = self.provider.prev_io_counters[self.iface].bytes_recv
recv = self.provider.io_counters[self.iface].bytes_recv
prevSent = self.provider.prev_io_counters[self.iface].bytes_sent
sent = self.provider.io_counters[self.iface].bytes_sent
recvDiff = recv - prevRecv
sentDiff = sent - prevSent
dt = self.provider.time - self.provider.prev_time
recvDiff /= dt
sentDiff /= dt
text += f"{humanSize(recvDiff)}{humanSize(sentDiff)}"
if state >= 4: # Counter
text += f"{humanSize(recv)}{humanSize(sent)}"
return text
@ -582,12 +636,25 @@ class NetworkProvider(MirrorProvider):
async def run(self) -> None:
await super().run()
while True:
# if_addrs: dict[str, list[psutil._common.snicaddr]] = psutil.net_if_addrs()
# io_counters: dict[str, psutil._common.snetio] = psutil.net_io_counters(pernic=True)
loop = asyncio.get_running_loop()
self.time = loop.time()
self.io_counters = psutil.net_io_counters(pernic=True)
while True:
# Separate TaskGroup in case it takes longer than one second,
# it doesn't fill the main TaskGroup
async with asyncio.TaskGroup() as tg:
self.prev_io_counters = self.io_counters
self.prev_time = self.time
# On-demand would only benefit if_addrs:
# stats are used to determine display,
# and we want to keep previous io_counters
# so displaying stats is ~instant.
self.time = loop.time()
self.if_stats = psutil.net_if_stats()
self.if_addrs = psutil.net_if_addrs()
self.io_counters = psutil.net_io_counters(pernic=True)
for iface in self.if_stats:
if iface not in self.sections:
section = NetworkProviderSection(
@ -595,6 +662,7 @@ class NetworkProvider(MirrorProvider):
)
self.sections[iface] = section
self.module.bar.addLongRunningTask(section.updateOnStateChange())
tg.create_task(self.updateIface(iface))
for iface, section in self.sections.items():
if iface not in self.if_stats: