Add stats sensors for core and supervisor (#89455)

* Add stats sensors for core and supervisor

* Update homeassistant/components/hassio/__init__.py
This commit is contained in:
Joakim Sørensen 2023-03-09 19:06:35 +01:00 committed by GitHub
parent 3a4ce260b4
commit 4f29e1e180
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 295 additions and 13 deletions

View file

@ -115,11 +115,13 @@ CONFIG_SCHEMA = vol.Schema(
DATA_CORE_INFO = "hassio_core_info"
DATA_CORE_STATS = "hassio_core_stats"
DATA_HOST_INFO = "hassio_host_info"
DATA_STORE = "hassio_store"
DATA_INFO = "hassio_info"
DATA_OS_INFO = "hassio_os_info"
DATA_SUPERVISOR_INFO = "hassio_supervisor_info"
DATA_SUPERVISOR_STATS = "hassio_supervisor_stats"
DATA_ADDONS_CHANGELOGS = "hassio_addons_changelogs"
DATA_ADDONS_INFO = "hassio_addons_info"
DATA_ADDONS_STATS = "hassio_addons_stats"
@ -301,6 +303,26 @@ def get_addons_stats(hass):
return hass.data.get(DATA_ADDONS_STATS)
@callback
@bind_hass
def get_core_stats(hass):
"""Return core stats.
Async friendly.
"""
return hass.data.get(DATA_CORE_STATS)
@callback
@bind_hass
def get_supervisor_stats(hass):
"""Return supervisor stats.
Async friendly.
"""
return hass.data.get(DATA_SUPERVISOR_STATS)
@callback
@bind_hass
def get_addons_changelogs(hass):
@ -747,8 +769,14 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
if self.is_hass_os:
new_data[DATA_KEY_OS] = get_os_info(self.hass)
new_data[DATA_KEY_CORE] = get_core_info(self.hass)
new_data[DATA_KEY_SUPERVISOR] = supervisor_info
new_data[DATA_KEY_CORE] = {
**(get_core_info(self.hass) or {}),
**get_core_stats(self.hass),
}
new_data[DATA_KEY_SUPERVISOR] = {
**supervisor_info,
**get_supervisor_stats(self.hass),
}
# If this is the initial refresh, register all addons and return the dict
if not self.data:
@ -805,12 +833,16 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
(
self.hass.data[DATA_INFO],
self.hass.data[DATA_CORE_INFO],
self.hass.data[DATA_CORE_STATS],
self.hass.data[DATA_SUPERVISOR_INFO],
self.hass.data[DATA_SUPERVISOR_STATS],
self.hass.data[DATA_OS_INFO],
) = await asyncio.gather(
self.hassio.get_info(),
self.hassio.get_core_info(),
self.hassio.get_core_stats(),
self.hassio.get_supervisor_info(),
self.hassio.get_supervisor_stats(),
self.hassio.get_os_info(),
)

View file

@ -319,6 +319,14 @@ class HassIO:
"""
return self.send_command(f"/addons/{addon}/info", method="get")
@api_data
def get_core_stats(self):
"""Return stats for the core.
This method returns a coroutine.
"""
return self.send_command("/core/stats", method="get")
@api_data
def get_addon_stats(self, addon):
"""Return stats for an Add-on.
@ -327,6 +335,14 @@ class HassIO:
"""
return self.send_command(f"/addons/{addon}/stats", method="get")
@api_data
def get_supervisor_stats(self):
"""Return stats for the supervisor.
This method returns a coroutine.
"""
return self.send_command("/supervisor/stats", method="get")
def get_addon_changelog(self, addon):
"""Return changelog for an Add-on.

View file

@ -18,9 +18,16 @@ from .const import (
ATTR_VERSION,
ATTR_VERSION_LATEST,
DATA_KEY_ADDONS,
DATA_KEY_CORE,
DATA_KEY_OS,
DATA_KEY_SUPERVISOR,
)
from .entity import (
HassioAddonEntity,
HassioCoreEntity,
HassioOSEntity,
HassioSupervisorEntity,
)
from .entity import HassioAddonEntity, HassioOSEntity
COMMON_ENTITY_DESCRIPTIONS = (
SensorEntityDescription(
@ -35,7 +42,7 @@ COMMON_ENTITY_DESCRIPTIONS = (
),
)
ADDON_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS + (
STATS_ENTITY_DESCRIPTIONS = (
SensorEntityDescription(
entity_registry_enabled_default=False,
key=ATTR_CPU_PERCENT,
@ -54,7 +61,10 @@ ADDON_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS + (
),
)
ADDON_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS + STATS_ENTITY_DESCRIPTIONS
CORE_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS
OS_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS
SUPERVISOR_ENTITY_DESCRIPTIONS = STATS_ENTITY_DESCRIPTIONS
async def async_setup_entry(
@ -65,7 +75,9 @@ async def async_setup_entry(
"""Sensor set up for Hass.io config entry."""
coordinator = hass.data[ADDONS_COORDINATOR]
entities: list[HassioOSSensor | HassioAddonSensor] = []
entities: list[
HassioOSSensor | HassioAddonSensor | CoreSensor | SupervisorSensor
] = []
for addon in coordinator.data[DATA_KEY_ADDONS].values():
for entity_description in ADDON_ENTITY_DESCRIPTIONS:
@ -77,6 +89,22 @@ async def async_setup_entry(
)
)
for entity_description in CORE_ENTITY_DESCRIPTIONS:
entities.append(
CoreSensor(
coordinator=coordinator,
entity_description=entity_description,
)
)
for entity_description in SUPERVISOR_ENTITY_DESCRIPTIONS:
entities.append(
SupervisorSensor(
coordinator=coordinator,
entity_description=entity_description,
)
)
if coordinator.is_hass_os:
for entity_description in OS_ENTITY_DESCRIPTIONS:
entities.append(
@ -107,3 +135,21 @@ class HassioOSSensor(HassioOSEntity, SensorEntity):
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[DATA_KEY_OS][self.entity_description.key]
class CoreSensor(HassioCoreEntity, SensorEntity):
"""Sensor to track a core attribute."""
@property
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[DATA_KEY_CORE][self.entity_description.key]
class SupervisorSensor(HassioSupervisorEntity, SensorEntity):
"""Sensor to track a supervisor attribute."""
@property
def native_value(self) -> str:
"""Return native value of entity."""
return self.coordinator.data[DATA_KEY_SUPERVISOR][self.entity_description.key]

View file

@ -120,6 +120,38 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
aioclient_mock.get(
"http://127.0.0.1/addons/test/info",

View file

@ -125,6 +125,38 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
aioclient_mock.get(
"http://127.0.0.1/addons/test/info",

View file

@ -226,6 +226,34 @@ async def test_api_addon_stats(
assert aioclient_mock.call_count == 1
async def test_api_core_stats(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Add-on stats."""
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={"result": "ok", "data": {"memory_percent": 0.01}},
)
data = await hassio_handler.get_core_stats()
assert data["memory_percent"] == 0.01
assert aioclient_mock.call_count == 1
async def test_api_supervisor_stats(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test setup with API Add-on stats."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={"result": "ok", "data": {"memory_percent": 0.01}},
)
data = await hassio_handler.get_supervisor_stats()
assert data["memory_percent"] == 0.01
assert aioclient_mock.call_count == 1
async def test_api_discovery_message(
hassio_handler: HassIO, aioclient_mock: AiohttpClientMocker
) -> None:

View file

@ -124,6 +124,38 @@ def mock_all(aioclient_mock, request, os_info):
],
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/addons/test/stats",
json={
@ -210,7 +242,7 @@ async def test_setup_api_ping(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert hass.components.hassio.get_core_info()["version_latest"] == "1.0.0"
assert hass.components.hassio.is_hassio()
@ -254,7 +286,7 @@ async def test_setup_api_push_api_data(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
assert aioclient_mock.mock_calls[1][2]["watchdog"]
@ -273,7 +305,7 @@ async def test_setup_api_push_api_data_server_host(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
assert not aioclient_mock.mock_calls[1][2]["watchdog"]
@ -290,7 +322,7 @@ async def test_setup_api_push_api_data_default(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
refresh_token = aioclient_mock.mock_calls[1][2]["refresh_token"]
@ -370,7 +402,7 @@ async def test_setup_api_existing_hassio_user(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
assert aioclient_mock.mock_calls[1][2]["refresh_token"] == token.token
@ -387,7 +419,7 @@ async def test_setup_core_push_timezone(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone"
with patch("homeassistant.util.dt.set_default_time_zone"):
@ -407,7 +439,7 @@ async def test_setup_hassio_no_additional_data(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert aioclient_mock.mock_calls[-1][3]["Authorization"] == "Bearer 123456"
@ -822,7 +854,7 @@ async def test_setup_hardware_integration(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 16
assert aioclient_mock.call_count == 18
assert len(mock_setup_entry.mock_calls) == 1

View file

@ -113,6 +113,38 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
aioclient_mock.get(
"http://127.0.0.1/addons/test/info",

View file

@ -127,6 +127,38 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/core/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get(
"http://127.0.0.1/supervisor/stats",
json={
"result": "ok",
"data": {
"cpu_percent": 0.99,
"memory_usage": 182611968,
"memory_limit": 3977146368,
"memory_percent": 4.59,
"network_rx": 362570232,
"network_tx": 82374138,
"blk_read": 46010945536,
"blk_write": 15051526144,
},
},
)
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
aioclient_mock.get(
"http://127.0.0.1/addons/test/info",