Skip to content

Commit 9557fff

Browse files
authored
Merge pull request #7 from astrayel/claude/fix-unraid-integration-bug-wIuR7
Add Docker aggregate sensors
2 parents f4a7cac + 5d40688 commit 9557fff

3 files changed

Lines changed: 106 additions & 2 deletions

File tree

‎custom_components/unraid_api/sensor.py‎

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,18 @@
2525
from homeassistant.helpers.device_registry import DeviceInfo
2626

2727
from . import _LOGGER
28-
from .const import CONF_DRIVES, CONF_SHARES, CONF_VMS, DOMAIN
28+
from .const import CONF_DOCKER_MODE, CONF_DRIVES, CONF_SHARES, CONF_VMS, DOCKER_MODE_OFF, DOMAIN
2929
from .entity import UnraidBaseEntity, UnraidEntityDescription
30-
from .models import Disk, DiskType, DockerContainer, Share, UpsDevice, VirtualMachine, VmState
30+
from .models import (
31+
ContainerState,
32+
Disk,
33+
DiskType,
34+
DockerContainer,
35+
Share,
36+
UpsDevice,
37+
VirtualMachine,
38+
VmState,
39+
)
3140

3241
if TYPE_CHECKING:
3342
from collections.abc import Callable
@@ -406,6 +415,35 @@ def calc_disk_usage_percentage(disk: Disk) -> StateType:
406415
),
407416
)
408417

418+
DOCKER_AGGREGATE_SENSOR_DESCRIPTIONS: tuple[UnraidSensorEntityDescription, ...] = (
419+
UnraidSensorEntityDescription(
420+
key="docker_running",
421+
state_class=SensorStateClass.MEASUREMENT,
422+
value_fn=lambda coordinator: sum(
423+
1
424+
for c in coordinator.data["docker_containers"].values()
425+
if c.state == ContainerState.RUNNING
426+
),
427+
entity_category=EntityCategory.DIAGNOSTIC,
428+
),
429+
UnraidSensorEntityDescription(
430+
key="docker_exited",
431+
state_class=SensorStateClass.MEASUREMENT,
432+
value_fn=lambda coordinator: sum(
433+
1
434+
for c in coordinator.data["docker_containers"].values()
435+
if c.state == ContainerState.EXITED
436+
),
437+
entity_category=EntityCategory.DIAGNOSTIC,
438+
),
439+
UnraidSensorEntityDescription(
440+
key="docker_total",
441+
state_class=SensorStateClass.MEASUREMENT,
442+
value_fn=lambda coordinator: len(coordinator.data["docker_containers"]),
443+
entity_category=EntityCategory.DIAGNOSTIC,
444+
),
445+
)
446+
409447
VM_AGGREGATE_SENSOR_DESCRIPTIONS: tuple[UnraidSensorEntityDescription, ...] = (
410448
UnraidSensorEntityDescription(
411449
key="vms_running",
@@ -435,6 +473,12 @@ async def async_setup_entry(
435473
for description in SENSOR_DESCRIPTIONS
436474
if description.min_version <= config_entry.runtime_data.coordinator.api_client.version
437475
]
476+
if config_entry.options.get(CONF_DOCKER_MODE, DOCKER_MODE_OFF) != DOCKER_MODE_OFF:
477+
entities.extend(
478+
UnraidSensor(description, config_entry)
479+
for description in DOCKER_AGGREGATE_SENSOR_DESCRIPTIONS
480+
if description.min_version <= config_entry.runtime_data.coordinator.api_client.version
481+
)
438482
if config_entry.options.get(CONF_VMS):
439483
entities.extend(
440484
UnraidSensor(description, config_entry)

‎custom_components/unraid_api/translations/en.json‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@
166166
"exited": "Exited"
167167
}
168168
},
169+
"docker_running": {
170+
"name": "Docker containers running"
171+
},
172+
"docker_exited": {
173+
"name": "Docker containers exited"
174+
},
175+
"docker_total": {
176+
"name": "Docker containers total"
177+
},
169178
"vm_state": {
170179
"name": "State",
171180
"state": {

‎tests/test_sensor.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,57 @@ async def test_docker_sensors_removed(
538538
assert hass.states.get("sensor.test_server_grafana_public_state")
539539

540540

541+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
542+
async def test_docker_aggregate_sensors(hass: HomeAssistant, mock_api_client: MagicMock) -> None:
543+
"""Test Docker aggregate sensors on the root device."""
544+
assert mock_api_client
545+
assert await setup_config_entry(hass)
546+
547+
state = hass.states.get("sensor.test_server_docker_containers_running")
548+
assert state.state == "2"
549+
550+
state = hass.states.get("sensor.test_server_docker_containers_exited")
551+
assert state.state == "1"
552+
553+
state = hass.states.get("sensor.test_server_docker_containers_total")
554+
assert state.state == "3"
555+
556+
557+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
558+
async def test_docker_aggregate_sensors_disabled(
559+
hass: HomeAssistant,
560+
mock_api_client: MagicMock, # noqa: ARG001
561+
) -> None:
562+
"""Test Docker aggregate sensors are absent when Docker monitoring is off."""
563+
assert await setup_config_entry(hass, options=MOCK_OPTION_DATA_DISABLED)
564+
565+
assert hass.states.get("sensor.test_server_docker_containers_running") is None
566+
assert hass.states.get("sensor.test_server_docker_containers_exited") is None
567+
assert hass.states.get("sensor.test_server_docker_containers_total") is None
568+
569+
570+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
571+
async def test_docker_aggregate_sensors_update(
572+
hass: HomeAssistant, mock_api_client: MagicMock
573+
) -> None:
574+
"""Test Docker aggregate sensors update when containers change."""
575+
api_client: MockApiClient = mock_api_client.return_value
576+
577+
config_entry = await setup_config_entry(hass)
578+
assert config_entry
579+
580+
assert hass.states.get("sensor.test_server_docker_containers_running").state == "2"
581+
assert hass.states.get("sensor.test_server_docker_containers_total").state == "3"
582+
583+
api_client.state.docker.pop(0)
584+
await config_entry.runtime_data.coordinator.async_refresh()
585+
await hass.async_block_till_done()
586+
587+
assert hass.states.get("sensor.test_server_docker_containers_running").state == "1"
588+
assert hass.states.get("sensor.test_server_docker_containers_exited").state == "1"
589+
assert hass.states.get("sensor.test_server_docker_containers_total").state == "2"
590+
591+
541592
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
542593
@pytest.mark.parametrize(("api_state"), API_STATES)
543594
async def test_vm_sensors(

0 commit comments

Comments
 (0)