11"""Client to interact with the Glances API."""
2- import asyncio
2+ from __future__ import annotations
3+
34import logging
5+ from typing import Any
46
57import httpx
68
79from . import exceptions
810
911_LOGGER = logging .getLogger (__name__ )
10- _RESOURCE = "{schema}://{host}:{port}/api/{version}"
1112
1213
13- class Glances ( object ) :
14+ class Glances :
1415 """A class for handling the data retrieval."""
1516
1617 def __init__ (
1718 self ,
18- host = "localhost" ,
19- port = 61208 ,
20- version = 2 ,
21- ssl = False ,
22- verify_ssl = True ,
23- username = None ,
24- password = None ,
25- httpx_client = None ,
19+ host : str = "localhost" ,
20+ port : int = 61208 ,
21+ version : int = 2 ,
22+ ssl : bool = False ,
23+ verify_ssl : bool = True ,
24+ username : str | None = None ,
25+ password : str | None = None ,
26+ httpx_client : httpx . AsyncClient | None = None ,
2627 ):
2728 """Initialize the connection."""
2829 schema = "https" if ssl else "http"
29- self .url = _RESOURCE .format (
30- schema = schema , host = host , port = port , version = version
31- )
32- self .plugins = None
33- self .values = None
30+ self .url = f"{ schema } ://{ host } :{ port } /api/{ version } "
31+ self .data : dict [str , Any ] = {}
32+ self .plugins : list [str ] = []
33+ self .values : Any | None = None
3434 self .username = username
3535 self .password = password
3636 self .verify_ssl = verify_ssl
3737 self .httpx_client = httpx_client
3838
39- async def get_data (self , endpoint ) :
39+ async def get_data (self , endpoint : str ) -> None :
4040 """Retrieve the data."""
41- url = "{ }/{}" . format ( self . url , endpoint )
41+ url = f" { self . url } /{ endpoint } "
4242
4343 httpx_client = (
4444 self .httpx_client
@@ -49,10 +49,10 @@ async def get_data(self, endpoint):
4949 try :
5050 async with httpx_client as client :
5151 if self .password is None :
52- response = await client .get (str ( url ) )
52+ response = await client .get (url )
5353 else :
5454 response = await client .get (
55- str ( url ) , auth = (self .username , self .password )
55+ url , auth = (self .username , self .password )
5656 )
5757 except (httpx .ConnectError , httpx .TimeoutException ):
5858 raise exceptions .GlancesApiConnectionError (f"Connection to { url } failed" )
@@ -61,21 +61,24 @@ async def get_data(self, endpoint):
6161 raise exceptions .GlancesApiAuthorizationError (
6262 "Please check your credentials"
6363 )
64-
64+ if response .status_code == httpx .codes .BAD_REQUEST :
65+ raise exceptions .GlancesApiNoDataAvailable (
66+ f"endpoint: '{ endpoint } ' is not valid"
67+ )
6568 if response .status_code == httpx .codes .OK :
6669 try :
6770 _LOGGER .debug (response .json ())
6871 if endpoint == "all" :
6972 self .data = response .json ()
70- if endpoint == "pluginslist" :
73+ elif endpoint == "pluginslist" :
7174 self .plugins = response .json ()
7275 except TypeError :
7376 _LOGGER .error ("Can not load data from Glances" )
7477 raise exceptions .GlancesApiConnectionError (
7578 "Unable to get the data from Glances"
7679 )
7780
78- async def get_metrics (self , element ) :
81+ async def get_metrics (self , element : str ) -> None :
7982 """Get all the metrics for a monitored element."""
8083 await self .get_data ("all" )
8184 await self .get_data ("pluginslist" )
@@ -84,3 +87,67 @@ async def get_metrics(self, element):
8487 self .values = self .data [element ]
8588 else :
8689 raise exceptions .GlancesApiError ("Element data not available" )
90+
91+ async def get_ha_sensor_data (self ) -> dict [str , Any ] | None :
92+ """Create a dictionary with data for Home Assistant sensors."""
93+ await self .get_data ("all" )
94+
95+ sensor_data : dict [str , Any ] = {}
96+
97+ if disks := self .data .get ("fs" ):
98+ sensor_data ["fs" ] = {}
99+ for disk in disks :
100+ disk_free = disk .get ("free" ) or (disk ["size" ] - disk ["used" ])
101+ sensor_data ["fs" ][disk ["mnt_point" ]] = {
102+ "disk_use" : round (disk ["used" ] / 1024 ** 3 , 1 ),
103+ "disk_use_percent" : disk ["percent" ],
104+ "disk_free" : round (disk_free / 1024 ** 3 , 1 ),
105+ }
106+ if data := self .data .get ("sensors" ):
107+ sensor_data ["sensors" ] = {}
108+ for sensor in data :
109+ sensor_data ["sensors" ][sensor ["label" ]] = {
110+ sensor ["type" ]: sensor ["value" ]
111+ }
112+ if data := self .data .get ("mem" ):
113+ sensor_data ["mem" ] = {
114+ "memory_use_percent" : data ["percent" ],
115+ "memory_use" : round (data ["used" ] / 1024 ** 2 , 1 ),
116+ "memory_free" : round (data ["free" ] / 1024 ** 2 , 1 ),
117+ }
118+ if data := self .data .get ("memswap" ):
119+ sensor_data ["memswap" ] = {
120+ "swap_use_percent" : data ["percent" ],
121+ "swap_use" : round (data ["used" ] / 1024 ** 3 , 1 ),
122+ "swap_free" : round (data ["free" ] / 1024 ** 3 , 1 ),
123+ }
124+ if data := self .data .get ("load" ):
125+ sensor_data ["load" ] = {
126+ "processor_load" : data .get ("min15" )
127+ or self .data ["cpu" ]["total" ] # to be checked
128+ }
129+ if data := self .data .get ("processcount" ):
130+ sensor_data ["processcount" ] = {
131+ "process_running" : data ["running" ],
132+ "process_total" : data ["total" ],
133+ "process_thread" : data ["thread" ],
134+ "process_sleeping" : data ["sleeping" ],
135+ }
136+ if data := self .data .get ("quicklook" ):
137+ sensor_data ["cpu" ] = {"cpu_use_percent" : data ["cpu" ]}
138+ if "docker" in self .data and (data := self .data ["docker" ].get ("containers" )):
139+ active_containers = [
140+ container for container in data if container ["Status" ] == "running"
141+ ]
142+ sensor_data ["docker" ] = {"docker_active" : len (active_containers )}
143+ cpu_use = 0.0
144+ for container in active_containers :
145+ cpu_use += container ["cpu" ]["total" ]
146+ sensor_data ["docker" ]["docker_cpu_use" ] = round (cpu_use , 1 )
147+ mem_use = 0.0
148+ for container in active_containers :
149+ mem_use += container ["memory" ]["usage" ]
150+ sensor_data ["docker" ]["docker_memory_use" ] = round (mem_use / 1024 ** 2 , 1 )
151+ if data := self .data .get ("raid" ):
152+ sensor_data ["raid" ] = data
153+ return sensor_data
0 commit comments