Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions homeassistant/components/hassio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,13 @@


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"
Expand Down Expand Up @@ -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 Addons stats.
Comment thread
ludeeus marked this conversation as resolved.
Outdated

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):
Expand Down Expand Up @@ -747,8 +769,14 @@ async def _async_update_data(self) -> dict[str, Any]:
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:
Expand Down Expand Up @@ -805,12 +833,16 @@ async def force_data_refresh(self) -> None:
(
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(),
)

Expand Down
16 changes: 16 additions & 0 deletions homeassistant/components/hassio/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,14 @@ def get_addon_info(self, addon):
"""
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.
Expand All @@ -327,6 +335,14 @@ def get_addon_stats(self, addon):
"""
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.

Expand Down
52 changes: 49 additions & 3 deletions homeassistant/components/hassio/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@
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(
Expand All @@ -35,7 +42,7 @@
),
)

ADDON_ENTITY_DESCRIPTIONS = COMMON_ENTITY_DESCRIPTIONS + (
STATS_ENTITY_DESCRIPTIONS = (
SensorEntityDescription(
entity_registry_enabled_default=False,
key=ATTR_CPU_PERCENT,
Expand All @@ -54,7 +61,10 @@
),
)

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(
Expand All @@ -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:
Expand All @@ -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(
Expand Down Expand Up @@ -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]
32 changes: 32 additions & 0 deletions tests/components/hassio/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
32 changes: 32 additions & 0 deletions tests/components/hassio/test_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
28 changes: 28 additions & 0 deletions tests/components/hassio/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading