Add a storage free space sensor for the Shelly Camera (#182192)

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Maciej Bieniek
2026-09-14 18:50:15 +02:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent 10ed311370
commit 8a9d5b7649
4 changed files with 87 additions and 1 deletions
@@ -59,6 +59,9 @@
"self_test": {
"default": "mdi:progress-wrench"
},
"storage_free_space": {
"default": "mdi:sd"
},
"tilt": {
"default": "mdi:angle-acute"
},
+13
View File
@@ -26,6 +26,7 @@ from homeassistant.const import (
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfFrequency,
UnitOfInformation,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
@@ -1732,6 +1733,18 @@ RPC_SENSORS: Final = {
and right.get("vial", {}).get("level", -1) != -1
),
),
"storage_fs_free": RpcSensorDescription(
key="storage",
sub_key="fs_free",
translation_key="storage_free_space",
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DATA_SIZE,
native_unit_of_measurement=UnitOfInformation.BYTES,
suggested_unit_of_measurement=UnitOfInformation.GIGABYTES,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
removal_condition=lambda _, status, key: not status[key]["present"],
),
}
@@ -527,6 +527,9 @@
"session_energy": {
"name": "Session energy"
},
"storage_free_space": {
"name": "Free storage space"
},
"temperature_with_channel_name": {
"name": "{channel_name} temperature"
},
+68 -1
View File
@@ -3,7 +3,7 @@
from copy import deepcopy
from unittest.mock import Mock, PropertyMock
from aioshelly.const import MODEL_BLU_GATEWAY_G3, MODEL_EM3
from aioshelly.const import MODEL_BLU_GATEWAY_G3, MODEL_CAMERA, MODEL_EM3
from aioshelly.exceptions import NotInitialized
from freezegun.api import FrozenDateTimeFactory
import pytest
@@ -34,6 +34,7 @@ from homeassistant.const import (
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfFrequency,
UnitOfInformation,
UnitOfPower,
UnitOfTemperature,
UnitOfVolume,
@@ -2326,3 +2327,69 @@ async def test_rpc_sensor_errors_none(
assert (state := hass.states.get("sensor.test_name_temperature"))
assert state.state == "11.1"
async def test_rpc_storage_fs_free_sensor(
hass: HomeAssistant,
mock_rpc_device: Mock,
entity_registry: EntityRegistry,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test RPC storage free space sensor."""
status = {"storage:0": {"present": True, "fs_free": 1048576000}}
monkeypatch.setattr(mock_rpc_device, "status", status)
config = {"storage:0": {"id": 0}}
monkeypatch.setattr(mock_rpc_device, "config", config)
entity_id = f"{SENSOR_DOMAIN}.test_name_free_storage_space"
await init_integration(hass, 4, model=MODEL_CAMERA)
assert (state := hass.states.get(entity_id))
assert state.state == "1.048576"
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfInformation.GIGABYTES
assert state.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.DATA_SIZE
assert state.attributes[ATTR_STATE_CLASS] == SensorStateClass.MEASUREMENT
assert (entry := entity_registry.async_get(entity_id))
assert entry.unique_id == "123456789ABC-storage:0-storage_fs_free"
assert entry.unit_of_measurement == UnitOfInformation.GIGABYTES
mutate_rpc_device_status(
monkeypatch, mock_rpc_device, "storage:0", "fs_free", 2097152000
)
mock_rpc_device.mock_update()
assert (state := hass.states.get(entity_id))
assert state.state == "2.097152"
async def test_rpc_storage_fs_free_sensor_removal(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
entity_registry: EntityRegistry,
device_registry: DeviceRegistry,
) -> None:
"""Test RPC storage free space sensor removal when storage not present."""
status = {"storage:0": {"present": False, "fs_free": 0}}
monkeypatch.setattr(mock_rpc_device, "status", status)
config = {"storage:0": {"id": 0}}
monkeypatch.setattr(mock_rpc_device, "config", config)
config_entry = await init_integration(hass, 4, model=MODEL_CAMERA, skip_setup=True)
device_entry = register_device(device_registry, config_entry)
entity_id = register_entity(
hass,
SENSOR_DOMAIN,
"test_name_free_storage_space",
"storage:0-storage_fs_free",
config_entry,
device_id=device_entry.id,
)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert entity_registry.async_get(entity_id) is None