From e7fedef651b1235f10e45a4180ffa56f0fd160c9 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 21 Nov 2024 08:31:50 +0100 Subject: [PATCH] Add created sensor in filesize (#131108) --- .../components/filesize/coordinator.py | 2 + homeassistant/components/filesize/icons.json | 3 + homeassistant/components/filesize/sensor.py | 7 + .../components/filesize/strings.json | 3 + tests/components/filesize/conftest.py | 15 +- .../filesize/snapshots/test_sensor.ambr | 197 ++++++++++++++++++ tests/components/filesize/test_sensor.py | 45 +++- 7 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 tests/components/filesize/snapshots/test_sensor.ambr diff --git a/homeassistant/components/filesize/coordinator.py b/homeassistant/components/filesize/coordinator.py index c0dbb14555e..8350cee91bf 100644 --- a/homeassistant/components/filesize/coordinator.py +++ b/homeassistant/components/filesize/coordinator.py @@ -60,12 +60,14 @@ class FileSizeCoordinator(DataUpdateCoordinator[dict[str, int | float | datetime statinfo = await self.hass.async_add_executor_job(self._update) size = statinfo.st_size last_updated = dt_util.utc_from_timestamp(statinfo.st_mtime) + created = dt_util.utc_from_timestamp(statinfo.st_ctime) _LOGGER.debug("size %s, last updated %s", size, last_updated) data: dict[str, int | float | datetime] = { "file": round(size / 1e6, 2), "bytes": size, "last_updated": last_updated, + "created": created, } return data diff --git a/homeassistant/components/filesize/icons.json b/homeassistant/components/filesize/icons.json index 15829589853..059a51a9e34 100644 --- a/homeassistant/components/filesize/icons.json +++ b/homeassistant/components/filesize/icons.json @@ -9,6 +9,9 @@ }, "last_updated": { "default": "mdi:file" + }, + "created": { + "default": "mdi:file" } } } diff --git a/homeassistant/components/filesize/sensor.py b/homeassistant/components/filesize/sensor.py index 71a4e50edfe..dfab815739b 100644 --- a/homeassistant/components/filesize/sensor.py +++ b/homeassistant/components/filesize/sensor.py @@ -47,6 +47,13 @@ SENSOR_TYPES = ( device_class=SensorDeviceClass.TIMESTAMP, entity_category=EntityCategory.DIAGNOSTIC, ), + SensorEntityDescription( + key="created", + translation_key="created", + entity_registry_enabled_default=False, + device_class=SensorDeviceClass.TIMESTAMP, + entity_category=EntityCategory.DIAGNOSTIC, + ), ) diff --git a/homeassistant/components/filesize/strings.json b/homeassistant/components/filesize/strings.json index 3323c3411b2..27d83d9fb62 100644 --- a/homeassistant/components/filesize/strings.json +++ b/homeassistant/components/filesize/strings.json @@ -26,6 +26,9 @@ }, "last_updated": { "name": "Last updated" + }, + "created": { + "name": "Created" } } } diff --git a/tests/components/filesize/conftest.py b/tests/components/filesize/conftest.py index ac66af0d22f..09acf7a58cc 100644 --- a/tests/components/filesize/conftest.py +++ b/tests/components/filesize/conftest.py @@ -8,21 +8,30 @@ from unittest.mock import patch import pytest -from homeassistant.components.filesize.const import DOMAIN -from homeassistant.const import CONF_FILE_PATH +from homeassistant.components.filesize.const import DOMAIN, PLATFORMS +from homeassistant.const import CONF_FILE_PATH, Platform from . import TEST_FILE_NAME from tests.common import MockConfigEntry +@pytest.fixture(name="load_platforms") +async def patch_platform_constant() -> list[Platform]: + """Return list of platforms to load.""" + return PLATFORMS + + @pytest.fixture -def mock_config_entry(tmp_path: Path) -> MockConfigEntry: +def mock_config_entry( + tmp_path: Path, load_platforms: list[Platform] +) -> MockConfigEntry: """Return the default mocked config entry.""" test_file = str(tmp_path.joinpath(TEST_FILE_NAME)) return MockConfigEntry( title=TEST_FILE_NAME, domain=DOMAIN, + entry_id="01JD5CTQMH9FKEFQKZJ8MMBQ3X", data={CONF_FILE_PATH: test_file}, unique_id=test_file, ) diff --git a/tests/components/filesize/snapshots/test_sensor.ambr b/tests/components/filesize/snapshots/test_sensor.ambr new file mode 100644 index 00000000000..fb3702e0255 --- /dev/null +++ b/tests/components/filesize/snapshots/test_sensor.ambr @@ -0,0 +1,197 @@ +# serializer version: 1 +# name: test_sensors[load_platforms0][sensor.file_txt_created-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.file_txt_created', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Created', + 'platform': 'filesize', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'created', + 'unique_id': '01JD5CTQMH9FKEFQKZJ8MMBQ3X-created', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_created-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'file.txt Created', + }), + 'context': , + 'entity_id': 'sensor.file_txt_created', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2024-11-20T18:19:04+00:00', + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_last_updated-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.file_txt_last_updated', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Last updated', + 'platform': 'filesize', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'last_updated', + 'unique_id': '01JD5CTQMH9FKEFQKZJ8MMBQ3X-last_updated', + 'unit_of_measurement': None, + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_last_updated-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'timestamp', + 'friendly_name': 'file.txt Last updated', + }), + 'context': , + 'entity_id': 'sensor.file_txt_last_updated', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '2024-11-20T18:19:24+00:00', + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_size-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.file_txt_size', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Size', + 'platform': 'filesize', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'size', + 'unique_id': '01JD5CTQMH9FKEFQKZJ8MMBQ3X', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_size-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'file.txt Size', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.file_txt_size', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '0.0', + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_size_in_bytes-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'state_class': , + }), + 'config_entry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': , + 'entity_id': 'sensor.file_txt_size_in_bytes', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Size in bytes', + 'platform': 'filesize', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'size_bytes', + 'unique_id': '01JD5CTQMH9FKEFQKZJ8MMBQ3X-bytes', + 'unit_of_measurement': , + }) +# --- +# name: test_sensors[load_platforms0][sensor.file_txt_size_in_bytes-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'data_size', + 'friendly_name': 'file.txt Size in bytes', + 'state_class': , + 'unit_of_measurement': , + }), + 'context': , + 'entity_id': 'sensor.file_txt_size_in_bytes', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '4', + }) +# --- diff --git a/tests/components/filesize/test_sensor.py b/tests/components/filesize/test_sensor.py index 880563f0ad8..62554b15b8e 100644 --- a/tests/components/filesize/test_sensor.py +++ b/tests/components/filesize/test_sensor.py @@ -2,14 +2,55 @@ import os from pathlib import Path +from unittest.mock import patch -from homeassistant.const import CONF_FILE_PATH, STATE_UNAVAILABLE +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.const import CONF_FILE_PATH, STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_component import async_update_entity from . import TEST_FILE_NAME, async_create_file -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.mark.parametrize( + "load_platforms", + [[Platform.SENSOR]], +) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_sensors( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + tmp_path: Path, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Test that an invalid path is caught.""" + testfile = str(tmp_path.joinpath("file.txt")) + await async_create_file(hass, testfile) + hass.config.allowlist_external_dirs = {tmp_path} + mock_config_entry.add_to_hass(hass) + hass.config_entries.async_update_entry( + mock_config_entry, data={CONF_FILE_PATH: testfile} + ) + with ( + patch( + "os.stat_result.st_mtime", + 1732126764.780758, + ), + patch( + "os.stat_result.st_ctime", + 1732126744.780758, + ), + ): + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) async def test_invalid_path(