fix: keep local telemetry and display independent of monitor
This commit is contained in:
+32
-18
@@ -5,6 +5,7 @@ import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from settings import COLORS,EFFECTS,GROUPS
|
||||
from local_state import LocalState
|
||||
|
||||
|
||||
def log(event,**fields):
|
||||
@@ -59,6 +60,7 @@ class Controller:
|
||||
def __init__(self,config,driver=None,clock=time.monotonic,temp_reader=temperature):
|
||||
self.config=config;self.driver=driver;self.clock=clock;self.temp_reader=temp_reader
|
||||
self.lock=threading.RLock();self.stop_event=threading.Event();self.threads=[]
|
||||
self.local_reader=LocalState();self.local_state={'system':{},'network':{}};self.local_sampled_at=None
|
||||
self.state=None;self.expires=0;self.ever_connected=False;self.first_seen={}
|
||||
self.owner='NORMAL_HOME';self.page='NORMAL_HOME';self.page_since=clock()
|
||||
self.fan_level=0;self.temp=None;self.errors={};self.i2c_errors=0;self.rgb_mode='OFF'
|
||||
@@ -88,7 +90,7 @@ class Controller:
|
||||
alerts=sorted(data['alerts'],key=lambda a:(-a['priority'],self.first_seen.get(a['id'],now),a['id']))
|
||||
byid={a['id']:a for a in alerts};owner=alerts[0]['id'] if alerts else 'NORMAL_HOME'
|
||||
if self.owner in byid and byid[self.owner]['priority']==byid[owner]['priority']:owner=self.owner
|
||||
if not connected and self.ever_connected:owner='DISCONNECTED'
|
||||
# External lease expiry clears only external alerts, never the local home.
|
||||
if owner!=self.owner:
|
||||
log('DISPLAY_OWNER',old=self.owner,new=owner)
|
||||
self.owner=owner;self.page=owner;self.page_since=now
|
||||
@@ -98,7 +100,7 @@ class Controller:
|
||||
self.page=pages[(pages.index(self.page)+1)%len(pages)];self.page_since=now
|
||||
rgb=byid.get(self.page) or byid.get(owner)
|
||||
return {'owner':owner,'page':self.page,'connected':connected,'alerts':alerts,'rgb':rgb,
|
||||
'system':data['system'],'network':data['network'],'config':self.config}
|
||||
'system':self.local_state['system'],'network':self.local_state['network'],'config':self.config}
|
||||
|
||||
def view(self):
|
||||
with self.lock:return copy.deepcopy(self._view_locked())
|
||||
@@ -109,7 +111,8 @@ class Controller:
|
||||
return {'available':self.driver is not None,'connected':v['connected'],'owner':v['owner'],
|
||||
'oled_page':v['page'],'rgb_mode':self.rgb_mode,'fan_level':self.fan_level,
|
||||
'fan_name':('OFF','L1','L2','L3','L4','MAX')[self.fan_level] if self.driver else 'UNAVAILABLE',
|
||||
'temperature_c':self.temp,'errors':dict(self.errors),'i2c_errors':self.i2c_errors}
|
||||
'temperature_c':self.temp,'local_state':copy.deepcopy(self.local_state),
|
||||
'local_sample_age_seconds':None if self.local_sampled_at is None else max(0,self.clock()-self.local_sampled_at),'errors':dict(self.errors),'i2c_errors':self.i2c_errors}
|
||||
|
||||
def error(self,worker,exc):
|
||||
with self.lock:
|
||||
@@ -124,9 +127,22 @@ class Controller:
|
||||
def start(self):
|
||||
if self.driver is None:return
|
||||
self.driver.off();self.driver.oled_init()
|
||||
for name,work in (('fan',self.fan_loop),('oled',self.oled_loop),('rgb',self.rgb_loop)):
|
||||
for name,work in (('telemetry',self.telemetry_loop),('fan',self.fan_loop),('oled',self.oled_loop),('rgb',self.rgb_loop)):
|
||||
thread=threading.Thread(target=work,name=name,daemon=True);self.threads.append(thread);thread.start()
|
||||
|
||||
def telemetry_loop(self):
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
state,errors=self.local_reader.sample()
|
||||
with self.lock:
|
||||
self.local_state=state;self.local_sampled_at=self.clock()
|
||||
for key in ('cpu_percent','memory_percent','disk_percent','temperature_c','network'):
|
||||
if key in errors:self.error('telemetry_'+key,errors[key])
|
||||
else:self.recovered('telemetry_'+key)
|
||||
self.recovered('telemetry')
|
||||
except Exception as exc:self.error('telemetry',exc)
|
||||
self.stop_event.wait(1)
|
||||
|
||||
def fan_loop(self):
|
||||
initialized=False
|
||||
while not self.stop_event.is_set():
|
||||
@@ -155,20 +171,19 @@ class Controller:
|
||||
while not self.stop_event.is_set():
|
||||
try:
|
||||
v=self.view();page=v['page'];system=v['system'];network=v['network'];home=False
|
||||
if page=='DISCONNECTED':lines=[('MONITOR OFFLINE',''),('STATE EXPIRED',''),('LOCAL FAN ACTIVE',''),('CHECK MONITOR','')]
|
||||
elif page=='NORMAL_HOME':
|
||||
if page=='NORMAL_HOME':
|
||||
home=True
|
||||
if not v['connected']:
|
||||
lines=[('STANDALONE',''),(f"TMP {self.temp:.1f}C" if self.temp is not None else 'TMP UNKNOWN',''),('FAN LOCAL',''),('NO MONITOR','')]
|
||||
else:
|
||||
ip=network.get('ip','NO IP');label=network.get('ip_label','IP4')
|
||||
if label=='IP6' and len(ip)>18:ip=ip[:7]+'..'+ip[-7:]
|
||||
metric=network.get('metric','');kind=network.get('kind','NET')
|
||||
try:signal=max(0,min(100,round((float(metric)+100)*2)))
|
||||
except ValueError:signal=None
|
||||
lines=[(f"CPU {system['cpu_percent']:.1f}%",f"MEM {system['memory_percent']:.1f}%"),
|
||||
(f"TMP {system['temperature_c']:.1f}C",'FAN '+('OFF','L1','L2','L3','L4','MAX')[self.fan_level]),
|
||||
(f"DSK {system['disk_percent']:.1f}%",f'WIF {signal}%' if kind=='WIF' and signal is not None else f'{kind} {metric}'),(label+' '+ip,'')]
|
||||
ip=network.get('ip','NO IP');label=network.get('ip_label','IP4')
|
||||
if label=='IP6' and len(ip)>18:ip=ip[:7]+'..'+ip[-7:]
|
||||
metric=network.get('metric','--');kind=network.get('kind','NET')
|
||||
try:signal=max(0,min(100,round((float(metric)+100)*2)))
|
||||
except (ValueError,TypeError):signal=None
|
||||
def value(key):
|
||||
n=system.get(key)
|
||||
return f'{n:.1f}' if isinstance(n,(float,int)) and math.isfinite(n) else '--'
|
||||
lines=[('CPU '+value('cpu_percent')+'%','MEM '+value('memory_percent')+'%'),
|
||||
('TMP '+value('temperature_c')+'C','FAN '+('OFF','L1','L2','L3','L4','MAX')[self.fan_level]),
|
||||
('DSK '+value('disk_percent')+'%',f'WIF {signal}%' if kind=='WIF' and signal is not None else f'{kind} {metric}'),(label+' '+ip,'')]
|
||||
else:
|
||||
a=next(a for a in v['alerts'] if a['id']==page);index=v['alerts'].index(a)+1
|
||||
lines=[(a['title'],f"{index}/{len(v['alerts'])}"),(a['l2'],''),(a['l3'],''),(a['l4'],'')]
|
||||
@@ -182,7 +197,6 @@ class Controller:
|
||||
@staticmethod
|
||||
def profile(view):
|
||||
c=view['config']['rgb'];a=view['rgb']
|
||||
if view['owner']=='DISCONNECTED':return ('off',),'DISCONNECTED_OFF'
|
||||
if a:
|
||||
group=a['category'];level=('warning','critical','emergency')[int(a['severity'])-1]
|
||||
label=f"{group}_{level}_{c['alert_mode']}".upper()
|
||||
|
||||
Reference in New Issue
Block a user