diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index 9c92724c5436b8..f11325b02bf033 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -20,10 +20,11 @@ from homeassistant.const import CONF_WEBHOOK_ID, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import ( - aiohttp_client, - config_entry_oauth2_flow, - config_validation as cv, +from homeassistant.helpers import aiohttp_client, config_validation as cv +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, + OAuth2Session, + async_get_config_entry_implementation, ) from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -73,17 +74,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Netatmo from a config entry.""" - implementation = ( - await config_entry_oauth2_flow.async_get_config_entry_implementation( - hass, entry - ) - ) + try: + implementation = await async_get_config_entry_implementation(hass, entry) + except ImplementationUnavailableError as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="oauth2_implementation_unavailable", + ) from err # Set unique id if non was set (migration) if not entry.unique_id: hass.config_entries.async_update_entry(entry, unique_id=DOMAIN) - session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) + session = OAuth2Session(hass, entry, implementation) try: await session.async_ensure_token_valid() except aiohttp.ClientResponseError as ex: diff --git a/homeassistant/components/netatmo/strings.json b/homeassistant/components/netatmo/strings.json index befc9ea843d4f7..0f53cb0dba06b3 100644 --- a/homeassistant/components/netatmo/strings.json +++ b/homeassistant/components/netatmo/strings.json @@ -143,6 +143,11 @@ } } }, + "exceptions": { + "oauth2_implementation_unavailable": { + "message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]" + } + }, "options": { "step": { "public_weather": { diff --git a/homeassistant/components/tesla_fleet/__init__.py b/homeassistant/components/tesla_fleet/__init__.py index db82251f5bbc85..b0189ba29d2e2f 100644 --- a/homeassistant/components/tesla_fleet/__init__.py +++ b/homeassistant/components/tesla_fleet/__init__.py @@ -23,6 +23,7 @@ from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, OAuth2Session, async_get_config_entry_implementation, ) @@ -61,6 +62,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) - try: implementation = await async_get_config_entry_implementation(hass, entry) + except ImplementationUnavailableError as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="oauth2_implementation_unavailable", + ) from err except ValueError as e: # Remove invalid implementation from config entry then raise AuthFailed hass.config_entries.async_update_entry( diff --git a/homeassistant/components/tesla_fleet/strings.json b/homeassistant/components/tesla_fleet/strings.json index 01f911679fcd65..53cee2e8142aac 100644 --- a/homeassistant/components/tesla_fleet/strings.json +++ b/homeassistant/components/tesla_fleet/strings.json @@ -609,6 +609,9 @@ "no_cable": { "message": "Charge cable will lock automatically when connected" }, + "oauth2_implementation_unavailable": { + "message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]" + }, "update_failed": { "message": "{endpoint} data request failed: {message}" } diff --git a/homeassistant/components/toon/__init__.py b/homeassistant/components/toon/__init__.py index 1c56b780c0fdb6..8e208ff80968f0 100644 --- a/homeassistant/components/toon/__init__.py +++ b/homeassistant/components/toon/__init__.py @@ -11,8 +11,10 @@ Platform, ) from homeassistant.core import CoreState, HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, OAuth2Session, async_get_config_entry_implementation, ) @@ -86,7 +88,13 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Toon from a config entry.""" - implementation = await async_get_config_entry_implementation(hass, entry) + try: + implementation = await async_get_config_entry_implementation(hass, entry) + except ImplementationUnavailableError as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="oauth2_implementation_unavailable", + ) from err session = OAuth2Session(hass, entry, implementation) coordinator = ToonDataUpdateCoordinator(hass, entry, session) diff --git a/homeassistant/components/toon/strings.json b/homeassistant/components/toon/strings.json index 32bf044206cbec..d92ff3ad2d0d90 100644 --- a/homeassistant/components/toon/strings.json +++ b/homeassistant/components/toon/strings.json @@ -32,6 +32,11 @@ } } }, + "exceptions": { + "oauth2_implementation_unavailable": { + "message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]" + } + }, "services": { "update": { "description": "Updates all entities with fresh data from Toon.", diff --git a/homeassistant/helpers/template/extensions/base.py b/homeassistant/helpers/template/extensions/base.py index 87a3625bdbb3a9..8fbe7312f55600 100644 --- a/homeassistant/helpers/template/extensions/base.py +++ b/homeassistant/helpers/template/extensions/base.py @@ -11,6 +11,7 @@ from jinja2.parser import Parser if TYPE_CHECKING: + from homeassistant.core import HomeAssistant from homeassistant.helpers.template import TemplateEnvironment @@ -26,6 +27,7 @@ class TemplateFunction: limited_ok: bool = ( True # Whether this function is available in limited environments ) + requires_hass: bool = False # Whether this function requires hass to be available class BaseTemplateExtension(Extension): @@ -44,6 +46,10 @@ def __init__( if functions: for template_func in functions: + # Skip functions that require hass when hass is not available + if template_func.requires_hass and self.environment.hass is None: + continue + # Skip functions not allowed in limited environments if self.environment.limited and not template_func.limited_ok: continue @@ -55,6 +61,24 @@ def __init__( if template_func.as_test: environment.tests[template_func.name] = template_func.func + @property + def hass(self) -> HomeAssistant: + """Return the Home Assistant instance. + + This property should only be used in extensions that have functions + marked with requires_hass=True, as it assumes hass is not None. + + Raises: + RuntimeError: If hass is not available in the environment. + """ + if self.environment.hass is None: + raise RuntimeError( + "Home Assistant instance is not available. " + "This property should only be used in extensions with " + "functions marked requires_hass=True." + ) + return self.environment.hass + def parse(self, parser: Parser) -> Node | list[Node]: """Required by Jinja2 Extension base class.""" return [] diff --git a/tests/components/growatt_server/conftest.py b/tests/components/growatt_server/conftest.py index ddee63e873d62c..5081533daf409e 100644 --- a/tests/components/growatt_server/conftest.py +++ b/tests/components/growatt_server/conftest.py @@ -37,7 +37,10 @@ def mock_growatt_v1_api(): Methods mocked for switch and number operations: - min_write_parameter: Called by switch/number entities to change settings """ - with patch("growattServer.OpenApiV1", autospec=True) as mock_v1_api_class: + with patch( + "homeassistant.components.growatt_server.config_flow.growattServer.OpenApiV1", + autospec=True, + ) as mock_v1_api_class: mock_v1_api = mock_v1_api_class.return_value # Called during setup to discover devices @@ -119,7 +122,10 @@ def mock_growatt_classic_api(): Methods mocked for device-specific tests: - tlx_detail: Provides TLX device data (kept for potential future tests) """ - with patch("growattServer.GrowattApi", autospec=True) as mock_classic_api_class: + with patch( + "homeassistant.components.growatt_server.config_flow.growattServer.GrowattApi", + autospec=True, + ) as mock_classic_api_class: # Use the autospec'd mock instance instead of creating a new Mock() mock_classic_api = mock_classic_api_class.return_value @@ -167,10 +173,10 @@ def mock_config_entry() -> MockConfigEntry: CONF_TOKEN: "test_token_123", CONF_URL: DEFAULT_URL, "user_id": "12345", - CONF_PLANT_ID: "plant_123", + CONF_PLANT_ID: "123456", "name": "Test Plant", }, - unique_id="plant_123", + unique_id="123456", ) @@ -188,10 +194,10 @@ def mock_config_entry_classic() -> MockConfigEntry: CONF_USERNAME: "test_user", CONF_PASSWORD: "test_password", CONF_URL: DEFAULT_URL, - CONF_PLANT_ID: "12345", + CONF_PLANT_ID: "123456", "name": "Test Plant", }, - unique_id="12345", + unique_id="123456", ) @@ -215,3 +221,13 @@ async def init_integration( await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.async_block_till_done() return mock_config_entry + + +@pytest.fixture +def mock_setup_entry(): + """Mock async_setup_entry to prevent actual setup during config flow tests.""" + with patch( + "homeassistant.components.growatt_server.async_setup_entry", + return_value=True, + ) as mock: + yield mock diff --git a/tests/components/growatt_server/snapshots/test_init.ambr b/tests/components/growatt_server/snapshots/test_init.ambr index 424c32d0410bef..d6a6e0cb84df20 100644 --- a/tests/components/growatt_server/snapshots/test_init.ambr +++ b/tests/components/growatt_server/snapshots/test_init.ambr @@ -61,65 +61,3 @@ 'via_device_id': None, }) # --- -# name: test_multiple_devices_discovered[device_min123456] - DeviceRegistryEntrySnapshot({ - 'area_id': None, - 'config_entries': , - 'config_entries_subentries': , - 'configuration_url': None, - 'connections': set({ - }), - 'disabled_by': None, - 'entry_type': None, - 'hw_version': None, - 'id': , - 'identifiers': set({ - tuple( - 'growatt_server', - 'MIN123456', - ), - }), - 'labels': set({ - }), - 'manufacturer': 'Growatt', - 'model': None, - 'model_id': None, - 'name': 'MIN123456', - 'name_by_user': None, - 'primary_config_entry': , - 'serial_number': None, - 'sw_version': None, - 'via_device_id': None, - }) -# --- -# name: test_multiple_devices_discovered[device_min789012] - DeviceRegistryEntrySnapshot({ - 'area_id': None, - 'config_entries': , - 'config_entries_subentries': , - 'configuration_url': None, - 'connections': set({ - }), - 'disabled_by': None, - 'entry_type': None, - 'hw_version': None, - 'id': , - 'identifiers': set({ - tuple( - 'growatt_server', - 'MIN789012', - ), - }), - 'labels': set({ - }), - 'manufacturer': 'Growatt', - 'model': None, - 'model_id': None, - 'name': 'MIN789012', - 'name_by_user': None, - 'primary_config_entry': , - 'serial_number': None, - 'sw_version': None, - 'via_device_id': None, - }) -# --- diff --git a/tests/components/growatt_server/snapshots/test_number.ambr b/tests/components/growatt_server/snapshots/test_number.ambr index 428756074b4a74..03e3cb878615d1 100644 --- a/tests/components/growatt_server/snapshots/test_number.ambr +++ b/tests/components/growatt_server/snapshots/test_number.ambr @@ -1,35 +1,4 @@ # serializer version: 1 -# name: test_number_device_registry - DeviceRegistryEntrySnapshot({ - 'area_id': None, - 'config_entries': , - 'config_entries_subentries': , - 'configuration_url': None, - 'connections': set({ - }), - 'disabled_by': None, - 'entry_type': None, - 'hw_version': None, - 'id': , - 'identifiers': set({ - tuple( - 'growatt_server', - 'MIN123456', - ), - }), - 'labels': set({ - }), - 'manufacturer': 'Growatt', - 'model': None, - 'model_id': None, - 'name': 'MIN123456', - 'name_by_user': None, - 'primary_config_entry': , - 'serial_number': None, - 'sw_version': None, - 'via_device_id': None, - }) -# --- # name: test_number_entities[number.min123456_battery_charge_power_limit-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/growatt_server/snapshots/test_sensor.ambr b/tests/components/growatt_server/snapshots/test_sensor.ambr index cf873f4a22e0b3..6a39175de957d2 100644 --- a/tests/components/growatt_server/snapshots/test_sensor.ambr +++ b/tests/components/growatt_server/snapshots/test_sensor.ambr @@ -3530,7 +3530,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_energy_today', - 'unique_id': 'plant_123-total_energy_today', + 'unique_id': '123456-total_energy_today', 'unit_of_measurement': , }) # --- @@ -3587,7 +3587,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_energy_output', - 'unique_id': 'plant_123-total_energy_output', + 'unique_id': '123456-total_energy_output', 'unit_of_measurement': , }) # --- @@ -3642,7 +3642,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_maximum_output', - 'unique_id': 'plant_123-total_maximum_output', + 'unique_id': '123456-total_maximum_output', 'unit_of_measurement': , }) # --- @@ -3693,7 +3693,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_money_total', - 'unique_id': 'plant_123-total_money_total', + 'unique_id': '123456-total_money_total', 'unit_of_measurement': None, }) # --- @@ -3747,7 +3747,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_output_power', - 'unique_id': 'plant_123-total_output_power', + 'unique_id': '123456-total_output_power', 'unit_of_measurement': , }) # --- @@ -3799,7 +3799,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_money_today', - 'unique_id': 'plant_123-total_money_today', + 'unique_id': '123456-total_money_today', 'unit_of_measurement': None, }) # --- @@ -3817,3824 +3817,6 @@ 'state': 'unknown', }) # --- -# name: test_sensor_entity_registry[sensor.min123456_ac_frequency-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_ac_frequency', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'AC frequency', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_frequency', - 'unique_id': 'MIN123456-tlx_frequency', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_ac_frequency-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'frequency', - 'friendly_name': 'MIN123456 AC frequency', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_ac_frequency', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_all_batteries_charged_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_all_batteries_charged_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'All batteries charged today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_all_batteries_charge_today', - 'unique_id': 'MIN123456-tlx_all_batteries_charge_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_all_batteries_charged_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 All batteries charged today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_all_batteries_charged_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_all_batteries_discharged_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_all_batteries_discharged_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'All batteries discharged today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_all_batteries_discharge_today', - 'unique_id': 'MIN123456-tlx_all_batteries_discharge_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_all_batteries_discharged_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 All batteries discharged today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_all_batteries_discharged_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_batteries_charged_from_grid_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_batteries_charged_from_grid_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Batteries charged from grid today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_batteries_charged_from_grid_today', - 'unique_id': 'MIN123456-tlx_batteries_charged_from_grid_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_batteries_charged_from_grid_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Batteries charged from grid today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_batteries_charged_from_grid_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_1_charging_w-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_battery_1_charging_w', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Battery 1 charging W', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_1_charge_w', - 'unique_id': 'MIN123456-tlx_battery_1_charge_w', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_1_charging_w-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Battery 1 charging W', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_battery_1_charging_w', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_1_discharging_w-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_battery_1_discharging_w', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Battery 1 discharging W', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_1_discharge_w', - 'unique_id': 'MIN123456-tlx_battery_1_discharge_w', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_1_discharging_w-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Battery 1 discharging W', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_battery_1_discharging_w', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_2_charging_w-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_battery_2_charging_w', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Battery 2 charging W', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_2_charge_w', - 'unique_id': 'MIN123456-tlx_battery_2_charge_w', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_2_charging_w-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Battery 2 charging W', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_battery_2_charging_w', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_2_discharging_w-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_battery_2_discharging_w', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Battery 2 discharging W', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_2_discharge_w', - 'unique_id': 'MIN123456-tlx_battery_2_discharge_w', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_battery_2_discharging_w-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Battery 2 discharging W', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_battery_2_discharging_w', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_energy_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Energy today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_today', - 'unique_id': 'MIN123456-tlx_energy_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Energy today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_energy_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_1-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_energy_today_input_1', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Energy today input 1', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_today_input_1', - 'unique_id': 'MIN123456-tlx_energy_today_input_1', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_1-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Energy today input 1', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_energy_today_input_1', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_2-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_energy_today_input_2', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Energy today input 2', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_today_input_2', - 'unique_id': 'MIN123456-tlx_energy_today_input_2', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_2-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Energy today input 2', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_energy_today_input_2', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_3-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_energy_today_input_3', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Energy today input 3', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_today_input_3', - 'unique_id': 'MIN123456-tlx_energy_today_input_3', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_3-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Energy today input 3', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_energy_today_input_3', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_4-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_energy_today_input_4', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Energy today input 4', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_today_input_4', - 'unique_id': 'MIN123456-tlx_energy_today_input_4', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_energy_today_input_4-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Energy today input 4', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_energy_today_input_4', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_export_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_export_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Export power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_pac_to_grid_total', - 'unique_id': 'MIN123456-tlx_pac_to_grid_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_export_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Export power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_export_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_export_to_grid_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_export_to_grid_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Export to grid today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_export_to_grid_today', - 'unique_id': 'MIN123456-tlx_export_to_grid_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_export_to_grid_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Export to grid today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_export_to_grid_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_import_from_grid_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_import_from_grid_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Import from grid today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_import_from_grid_today', - 'unique_id': 'MIN123456-tlx_import_from_grid_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_import_from_grid_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Import from grid today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_import_from_grid_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_import_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_import_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Import power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_pac_to_user_total', - 'unique_id': 'MIN123456-tlx_pac_to_user_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_import_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Import power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_import_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_1_amperage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_1_amperage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 1 amperage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_amperage_input_1', - 'unique_id': 'MIN123456-tlx_amperage_input_1', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_1_amperage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'current', - 'friendly_name': 'MIN123456 Input 1 amperage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_1_amperage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_1_voltage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_1_voltage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 1 voltage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_voltage_input_1', - 'unique_id': 'MIN123456-tlx_voltage_input_1', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_1_voltage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'voltage', - 'friendly_name': 'MIN123456 Input 1 voltage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_1_voltage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_1_wattage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_1_wattage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 1 wattage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_wattage_input_1', - 'unique_id': 'MIN123456-tlx_wattage_input_1', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_1_wattage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Input 1 wattage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_1_wattage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_2_amperage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_2_amperage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 2 amperage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_amperage_input_2', - 'unique_id': 'MIN123456-tlx_amperage_input_2', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_2_amperage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'current', - 'friendly_name': 'MIN123456 Input 2 amperage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_2_amperage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_2_voltage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_2_voltage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 2 voltage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_voltage_input_2', - 'unique_id': 'MIN123456-tlx_voltage_input_2', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_2_voltage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'voltage', - 'friendly_name': 'MIN123456 Input 2 voltage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_2_voltage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_2_wattage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_2_wattage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 2 wattage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_wattage_input_2', - 'unique_id': 'MIN123456-tlx_wattage_input_2', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_2_wattage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Input 2 wattage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_2_wattage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_3_amperage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_3_amperage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 3 amperage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_amperage_input_3', - 'unique_id': 'MIN123456-tlx_amperage_input_3', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_3_amperage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'current', - 'friendly_name': 'MIN123456 Input 3 amperage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_3_amperage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_3_voltage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_3_voltage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 3 voltage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_voltage_input_3', - 'unique_id': 'MIN123456-tlx_voltage_input_3', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_3_voltage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'voltage', - 'friendly_name': 'MIN123456 Input 3 voltage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_3_voltage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_3_wattage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_3_wattage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 3 wattage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_wattage_input_3', - 'unique_id': 'MIN123456-tlx_wattage_input_3', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_3_wattage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Input 3 wattage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_3_wattage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_4_amperage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_4_amperage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 4 amperage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_amperage_input_4', - 'unique_id': 'MIN123456-tlx_amperage_input_4', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_4_amperage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'current', - 'friendly_name': 'MIN123456 Input 4 amperage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_4_amperage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_4_voltage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_4_voltage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 4 voltage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_voltage_input_4', - 'unique_id': 'MIN123456-tlx_voltage_input_4', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_4_voltage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'voltage', - 'friendly_name': 'MIN123456 Input 4 voltage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_4_voltage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_4_wattage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_input_4_wattage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Input 4 wattage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_wattage_input_4', - 'unique_id': 'MIN123456-tlx_wattage_input_4', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_input_4_wattage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Input 4 wattage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_input_4_wattage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_internal_wattage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_internal_wattage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Internal wattage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_internal_wattage', - 'unique_id': 'MIN123456-tlx_internal_wattage', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_internal_wattage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Internal wattage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_internal_wattage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_batteries_charged_from_grid-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_batteries_charged_from_grid', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime batteries charged from grid', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_batteries_charged_from_grid_total', - 'unique_id': 'MIN123456-tlx_batteries_charged_from_grid_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_batteries_charged_from_grid-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime batteries charged from grid', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_batteries_charged_from_grid', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_energy_output-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_energy_output', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime energy output', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_total', - 'unique_id': 'MIN123456-tlx_energy_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_energy_output-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime energy output', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_energy_output', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_import_from_grid-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_import_from_grid', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime import from grid', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_import_from_grid_total', - 'unique_id': 'MIN123456-tlx_import_from_grid_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_import_from_grid-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime import from grid', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_import_from_grid', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_self_consumption-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_self_consumption', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime self consumption', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_self_consumption_total', - 'unique_id': 'MIN123456-tlx_self_consumption_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_self_consumption-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime self consumption', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_self_consumption', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_system_production-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_system_production', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime system production', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_system_production_total', - 'unique_id': 'MIN123456-tlx_system_production_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_system_production-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime system production', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_system_production', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_all_batteries_charged-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_all_batteries_charged', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total all batteries charged', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_all_batteries_charge_total', - 'unique_id': 'MIN123456-tlx_all_batteries_charge_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_all_batteries_charged-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total all batteries charged', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_all_batteries_charged', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_all_batteries_discharged-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_all_batteries_discharged', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total all batteries discharged', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_all_batteries_discharge_total', - 'unique_id': 'MIN123456-tlx_all_batteries_discharge_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_all_batteries_discharged-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total all batteries discharged', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_all_batteries_discharged', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_1_charged-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_battery_1_charged', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total battery 1 charged', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_1_charge_total', - 'unique_id': 'MIN123456-tlx_battery_1_charge_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_1_charged-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total battery 1 charged', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_battery_1_charged', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_1_discharged-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_battery_1_discharged', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total battery 1 discharged', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_1_discharge_total', - 'unique_id': 'MIN123456-tlx_battery_1_discharge_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_1_discharged-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total battery 1 discharged', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_battery_1_discharged', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_2_charged-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_battery_2_charged', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total battery 2 charged', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_2_charge_total', - 'unique_id': 'MIN123456-tlx_battery_2_charge_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_2_charged-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total battery 2 charged', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_battery_2_charged', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_2_discharged-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_battery_2_discharged', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total battery 2 discharged', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_battery_2_discharge_total', - 'unique_id': 'MIN123456-tlx_battery_2_discharge_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_battery_2_discharged-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total battery 2 discharged', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_battery_2_discharged', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_1-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_1', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total energy input 1', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_total_input_1', - 'unique_id': 'MIN123456-tlx_energy_total_input_1', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_1-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total energy input 1', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_1', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_2-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_2', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total energy input 2', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_total_input_2', - 'unique_id': 'MIN123456-tlx_energy_total_input_2', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_2-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total energy input 2', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_2', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_3-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_3', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total energy input 3', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_total_input_3', - 'unique_id': 'MIN123456-tlx_energy_total_input_3', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_3-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total energy input 3', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_3', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_4-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_4', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total energy input 4', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_energy_total_input_4', - 'unique_id': 'MIN123456-tlx_energy_total_input_4', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_energy_input_4-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total energy input 4', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_energy_input_4', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_export_to_grid-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_export_to_grid', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total export to grid', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_export_to_grid_total', - 'unique_id': 'MIN123456-tlx_export_to_grid_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_export_to_grid-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total export to grid', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_export_to_grid', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_load_consumption-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_load_consumption', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total load consumption', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'mix_load_consumption_total', - 'unique_id': 'MIN123456-mix_load_consumption_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_load_consumption-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total load consumption', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_load_consumption', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_solar_energy-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_lifetime_total_solar_energy', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime total solar energy', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_solar_generation_total', - 'unique_id': 'MIN123456-tlx_solar_generation_total', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_lifetime_total_solar_energy-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Lifetime total solar energy', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_lifetime_total_solar_energy', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_load_consumption_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_load_consumption_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Load consumption today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_load_consumption_today', - 'unique_id': 'MIN123456-tlx_load_consumption_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_load_consumption_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Load consumption today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_load_consumption_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_local_load_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_local_load_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Local load power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_pac_to_local_load', - 'unique_id': 'MIN123456-tlx_pac_to_local_load', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_local_load_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Local load power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_local_load_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_output_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_output_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Output power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_current_wattage', - 'unique_id': 'MIN123456-tlx_current_wattage', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_output_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Output power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_output_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_reactive_voltage-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_reactive_voltage', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Reactive voltage', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_reactive_voltage', - 'unique_id': 'MIN123456-tlx_reactive_voltage', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_reactive_voltage-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'voltage', - 'friendly_name': 'MIN123456 Reactive voltage', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_reactive_voltage', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_self_consumption_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_self_consumption_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Self consumption today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_self_consumption_today', - 'unique_id': 'MIN123456-tlx_self_consumption_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_self_consumption_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Self consumption today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_self_consumption_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_self_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_self_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Self power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_p_self', - 'unique_id': 'MIN123456-tlx_p_self', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_self_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 Self power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_self_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_solar_energy_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_solar_energy_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Solar energy today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_solar_generation_today', - 'unique_id': 'MIN123456-tlx_solar_generation_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_solar_energy_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 Solar energy today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_solar_energy_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_state_of_charge_soc-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_state_of_charge_soc', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'State of charge (SoC)', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_statement_of_charge', - 'unique_id': 'MIN123456-tlx_statement_of_charge', - 'unit_of_measurement': '%', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_state_of_charge_soc-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'battery', - 'friendly_name': 'MIN123456 State of charge (SoC)', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': '%', - }), - 'context': , - 'entity_id': 'sensor.min123456_state_of_charge_soc', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_system_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_system_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'System power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_p_system', - 'unique_id': 'MIN123456-tlx_p_system', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_system_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'MIN123456 System power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_system_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_system_production_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_system_production_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'System production today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_system_production_today', - 'unique_id': 'MIN123456-tlx_system_production_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_system_production_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'MIN123456 System production today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_system_production_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_1-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_temperature_1', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Temperature 1', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_temperature_1', - 'unique_id': 'MIN123456-tlx_temperature_1', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_1-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'MIN123456 Temperature 1', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_temperature_1', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_2-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_temperature_2', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Temperature 2', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_temperature_2', - 'unique_id': 'MIN123456-tlx_temperature_2', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_2-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'MIN123456 Temperature 2', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_temperature_2', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_3-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_temperature_3', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Temperature 3', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_temperature_3', - 'unique_id': 'MIN123456-tlx_temperature_3', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_3-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'MIN123456 Temperature 3', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_temperature_3', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_4-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_temperature_4', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Temperature 4', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_temperature_4', - 'unique_id': 'MIN123456-tlx_temperature_4', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_4-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'MIN123456 Temperature 4', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_temperature_4', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_5-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.min123456_temperature_5', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 1, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Temperature 5', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'tlx_temperature_5', - 'unique_id': 'MIN123456-tlx_temperature_5', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.min123456_temperature_5-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'temperature', - 'friendly_name': 'MIN123456 Temperature 5', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.min123456_temperature_5', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_energy_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.test_plant_total_energy_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Energy today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'total_energy_today', - 'unique_id': 'plant_123-total_energy_today', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_energy_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'Test Plant Total Energy today', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.test_plant_total_energy_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': '12.5', - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_lifetime_energy_output-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.test_plant_total_lifetime_energy_output', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 2, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Lifetime energy output', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'total_energy_output', - 'unique_id': 'plant_123-total_energy_output', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_lifetime_energy_output-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'energy', - 'friendly_name': 'Test Plant Total Lifetime energy output', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.test_plant_total_lifetime_energy_output', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': '1250.0', - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_maximum_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.test_plant_total_maximum_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Maximum power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'total_maximum_output', - 'unique_id': 'plant_123-total_maximum_output', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_maximum_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'Test Plant Total Maximum power', - 'icon': 'mdi:solar-power', - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.test_plant_total_maximum_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_money_lifetime-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.test_plant_total_money_lifetime', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': 'mdi:solar-power', - 'original_name': 'Money lifetime', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'total_money_total', - 'unique_id': 'plant_123-total_money_total', - 'unit_of_measurement': None, - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_money_lifetime-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Test Plant Total Money lifetime', - 'icon': 'mdi:solar-power', - }), - 'context': , - 'entity_id': 'sensor.test_plant_total_money_lifetime', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_output_power-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': dict({ - 'state_class': , - }), - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.test_plant_total_output_power', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - 'sensor': dict({ - 'suggested_display_precision': 0, - }), - }), - 'original_device_class': , - 'original_icon': 'mdi:solar-power', - 'original_name': 'Output power', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'total_output_power', - 'unique_id': 'plant_123-total_output_power', - 'unit_of_measurement': , - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_output_power-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'device_class': 'power', - 'friendly_name': 'Test Plant Total Output power', - 'icon': 'mdi:solar-power', - 'state_class': , - 'unit_of_measurement': , - }), - 'context': , - 'entity_id': 'sensor.test_plant_total_output_power', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': '2500', - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_total_money_today-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'sensor', - 'entity_category': None, - 'entity_id': 'sensor.test_plant_total_total_money_today', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': 'mdi:solar-power', - 'original_name': 'Total money today', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'total_money_today', - 'unique_id': 'plant_123-total_money_today', - 'unit_of_measurement': None, - }) -# --- -# name: test_sensor_entity_registry[sensor.test_plant_total_total_money_today-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Test Plant Total Total money today', - 'icon': 'mdi:solar-power', - }), - 'context': , - 'entity_id': 'sensor.test_plant_total_total_money_today', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'unknown', - }) -# --- # name: test_total_sensors_classic_api[sensor.test_plant_total_energy_today-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -7671,7 +3853,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_energy_today', - 'unique_id': '12345-total_energy_today', + 'unique_id': '123456-total_energy_today', 'unit_of_measurement': , }) # --- @@ -7728,7 +3910,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_energy_output', - 'unique_id': '12345-total_energy_output', + 'unique_id': '123456-total_energy_output', 'unit_of_measurement': , }) # --- @@ -7783,7 +3965,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_maximum_output', - 'unique_id': '12345-total_maximum_output', + 'unique_id': '123456-total_maximum_output', 'unit_of_measurement': , }) # --- @@ -7834,7 +4016,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_money_total', - 'unique_id': '12345-total_money_total', + 'unique_id': '123456-total_money_total', 'unit_of_measurement': 'USD', }) # --- @@ -7889,7 +4071,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_output_power', - 'unique_id': '12345-total_output_power', + 'unique_id': '123456-total_output_power', 'unit_of_measurement': , }) # --- @@ -7941,7 +4123,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'total_money_today', - 'unique_id': '12345-total_money_today', + 'unique_id': '123456-total_money_today', 'unit_of_measurement': 'USD', }) # --- diff --git a/tests/components/growatt_server/snapshots/test_switch.ambr b/tests/components/growatt_server/snapshots/test_switch.ambr index d6c3b35b9764c5..d3d7d981bbb314 100644 --- a/tests/components/growatt_server/snapshots/test_switch.ambr +++ b/tests/components/growatt_server/snapshots/test_switch.ambr @@ -1,35 +1,4 @@ # serializer version: 1 -# name: test_switch_device_registry - DeviceRegistryEntrySnapshot({ - 'area_id': None, - 'config_entries': , - 'config_entries_subentries': , - 'configuration_url': None, - 'connections': set({ - }), - 'disabled_by': None, - 'entry_type': None, - 'hw_version': None, - 'id': , - 'identifiers': set({ - tuple( - 'growatt_server', - 'MIN123456', - ), - }), - 'labels': set({ - }), - 'manufacturer': 'Growatt', - 'model': None, - 'model_id': None, - 'name': 'MIN123456', - 'name_by_user': None, - 'primary_config_entry': , - 'serial_number': None, - 'sw_version': None, - 'via_device_id': None, - }) -# --- # name: test_switch_entities[switch.min123456_charge_from_grid-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ @@ -78,51 +47,3 @@ 'state': 'on', }) # --- -# name: test_switch_entity_attributes[entity_entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - 'area_id': None, - 'capabilities': None, - 'config_entry_id': , - 'config_subentry_id': , - 'device_class': None, - 'device_id': , - 'disabled_by': None, - 'domain': 'switch', - 'entity_category': , - 'entity_id': 'switch.min123456_charge_from_grid', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': 'Charge from grid', - 'platform': 'growatt_server', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'ac_charge', - 'unique_id': 'MIN123456_ac_charge', - 'unit_of_measurement': None, - }) -# --- -# name: test_switch_entity_attributes[state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'MIN123456 Charge from grid', - }), - 'context': , - 'entity_id': 'switch.min123456_charge_from_grid', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'on', - }) -# --- diff --git a/tests/components/growatt_server/test_config_flow.py b/tests/components/growatt_server/test_config_flow.py index 746511ed0bee47..b61866cf038caa 100644 --- a/tests/components/growatt_server/test_config_flow.py +++ b/tests/components/growatt_server/test_config_flow.py @@ -1,7 +1,6 @@ """Tests for the Growatt server config flow.""" from copy import deepcopy -from unittest.mock import patch import growattServer import pytest @@ -133,8 +132,16 @@ async def test_auth_form_display( assert field in result["data_schema"].schema -async def test_password_auth_incorrect_login(hass: HomeAssistant) -> None: +async def test_password_auth_incorrect_login( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with incorrect credentials, then recovery.""" + # Simulate incorrect login + mock_growatt_classic_api.login.return_value = { + "msg": LOGIN_INVALID_AUTH_CODE, + "success": False, + } + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -144,33 +151,21 @@ async def test_password_auth_incorrect_login(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "password_auth"} ) - with patch( - "growattServer.GrowattApi.login", - return_value={"msg": LOGIN_INVALID_AUTH_CODE, "success": False}, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "password_auth" assert result["errors"] == {"base": ERROR_INVALID_AUTH} - # Test recovery - retry with correct credentials - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - return_value=GROWATT_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + # Test recovery - repatch for correct credentials + mock_growatt_classic_api.login.return_value = GROWATT_LOGIN_RESPONSE + mock_growatt_classic_api.plant_list.return_value = GROWATT_PLANT_LIST_RESPONSE + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT_PASSWORD[CONF_USERNAME] @@ -179,8 +174,13 @@ async def test_password_auth_incorrect_login(hass: HomeAssistant) -> None: assert result["data"][CONF_AUTH_TYPE] == AUTH_PASSWORD -async def test_password_auth_no_plants(hass: HomeAssistant) -> None: +async def test_password_auth_no_plants( + hass: HomeAssistant, mock_growatt_classic_api +) -> None: """Test password authentication with no plants.""" + # Repatch to return empty plants + mock_growatt_classic_api.plant_list.return_value = {"data": []} + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -190,20 +190,19 @@ async def test_password_auth_no_plants(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "password_auth"} ) - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch("growattServer.GrowattApi.plant_list", return_value={"data": []}), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == ABORT_NO_PLANTS -async def test_token_auth_no_plants(hass: HomeAssistant) -> None: +async def test_token_auth_no_plants(hass: HomeAssistant, mock_growatt_v1_api) -> None: """Test token authentication with no plants.""" + # Repatch to return empty plants + mock_growatt_v1_api.plant_list.return_value = {"plants": []} + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -213,17 +212,21 @@ async def test_token_auth_no_plants(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "token_auth"} ) - with patch("growattServer.OpenApiV1.plant_list", return_value={"plants": []}): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == ABORT_NO_PLANTS -async def test_password_auth_single_plant(hass: HomeAssistant) -> None: +async def test_password_auth_single_plant( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with single plant.""" + # Repatch plant_list with full plant data for config flow + mock_growatt_classic_api.plant_list.return_value = GROWATT_PLANT_LIST_RESPONSE + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -233,20 +236,9 @@ async def test_password_auth_single_plant(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "password_auth"} ) - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - return_value=GROWATT_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT_PASSWORD[CONF_USERNAME] @@ -257,17 +249,11 @@ async def test_password_auth_single_plant(hass: HomeAssistant) -> None: assert result["result"].unique_id == "123456" -async def test_password_auth_multiple_plants(hass: HomeAssistant) -> None: +async def test_password_auth_multiple_plants( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with multiple plants.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - # Select password authentication - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {"next_step_id": "password_auth"} - ) - + # Repatch plant_list with multiple plants plant_list = deepcopy(GROWATT_PLANT_LIST_RESPONSE) plant_list["data"].append( { @@ -280,29 +266,31 @@ async def test_password_auth_multiple_plants(hass: HomeAssistant) -> None: "currentPower": "420.0 W", } ) + mock_growatt_classic_api.plant_list.return_value = plant_list - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch("growattServer.GrowattApi.plant_list", return_value=plant_list), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) - - # Should show plant selection form - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "plant" - - # Select first plant - user_input = {CONF_PLANT_ID: "123456"} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + # Select password authentication + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"next_step_id": "password_auth"} + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) + + # Should show plant selection form + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "plant" + + # Select first plant + user_input = {CONF_PLANT_ID: "123456"} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input + ) + await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT_PASSWORD[CONF_USERNAME] @@ -315,7 +303,9 @@ async def test_password_auth_multiple_plants(hass: HomeAssistant) -> None: # Token authentication tests -async def test_token_auth_api_error(hass: HomeAssistant) -> None: +async def test_token_auth_api_error( + hass: HomeAssistant, mock_growatt_v1_api, mock_setup_entry +) -> None: """Test token authentication with API error, then recovery.""" result = await hass.config_entries.flow.async_init( @@ -329,30 +319,23 @@ async def test_token_auth_api_error(hass: HomeAssistant) -> None: # Any GrowattV1ApiError during token verification should result in invalid_auth error = growattServer.GrowattV1ApiError("API error") error.error_code = 100 + mock_growatt_v1_api.plant_list.side_effect = error - with patch("growattServer.OpenApiV1.plant_list", side_effect=error): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "token_auth" assert result["errors"] == {"base": ERROR_INVALID_AUTH} - # Test recovery - retry with valid token - with ( - patch( - "growattServer.OpenApiV1.plant_list", - return_value=GROWATT_V1_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + # Test recovery - reset side_effect and set normal return value + mock_growatt_v1_api.plant_list.side_effect = None + mock_growatt_v1_api.plant_list.return_value = GROWATT_V1_PLANT_LIST_RESPONSE + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_TOKEN] == FIXTURE_USER_INPUT_TOKEN[CONF_TOKEN] @@ -360,7 +343,9 @@ async def test_token_auth_api_error(hass: HomeAssistant) -> None: assert result["data"][CONF_AUTH_TYPE] == AUTH_API_TOKEN -async def test_token_auth_connection_error(hass: HomeAssistant) -> None: +async def test_token_auth_connection_error( + hass: HomeAssistant, mock_growatt_v1_api, mock_setup_entry +) -> None: """Test token authentication with network error, then recovery.""" result = await hass.config_entries.flow.async_init( @@ -371,32 +356,26 @@ async def test_token_auth_connection_error(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "token_auth"} ) - with patch( - "growattServer.OpenApiV1.plant_list", - side_effect=requests.exceptions.ConnectionError("Network error"), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + # Simulate connection error on first attempt + mock_growatt_v1_api.plant_list.side_effect = requests.exceptions.ConnectionError( + "Network error" + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "token_auth" assert result["errors"] == {"base": ERROR_CANNOT_CONNECT} - # Test recovery - retry when network is available - with ( - patch( - "growattServer.OpenApiV1.plant_list", - return_value=GROWATT_V1_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + # Test recovery - reset side_effect and set normal return value + mock_growatt_v1_api.plant_list.side_effect = None + mock_growatt_v1_api.plant_list.return_value = GROWATT_V1_PLANT_LIST_RESPONSE + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_TOKEN] == FIXTURE_USER_INPUT_TOKEN[CONF_TOKEN] @@ -404,7 +383,9 @@ async def test_token_auth_connection_error(hass: HomeAssistant) -> None: assert result["data"][CONF_AUTH_TYPE] == AUTH_API_TOKEN -async def test_token_auth_invalid_response(hass: HomeAssistant) -> None: +async def test_token_auth_invalid_response( + hass: HomeAssistant, mock_growatt_v1_api, mock_setup_entry +) -> None: """Test token authentication with invalid response format, then recovery.""" result = await hass.config_entries.flow.async_init( @@ -415,32 +396,23 @@ async def test_token_auth_invalid_response(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "token_auth"} ) - with patch( - "growattServer.OpenApiV1.plant_list", - return_value=None, # Invalid response format - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + # Return invalid response format (None instead of dict with 'plants' key) + mock_growatt_v1_api.plant_list.return_value = None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "token_auth" assert result["errors"] == {"base": ERROR_CANNOT_CONNECT} - # Test recovery - retry with valid response - with ( - patch( - "growattServer.OpenApiV1.plant_list", - return_value=GROWATT_V1_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + # Test recovery - set normal return value + mock_growatt_v1_api.plant_list.return_value = GROWATT_V1_PLANT_LIST_RESPONSE + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_TOKEN] == FIXTURE_USER_INPUT_TOKEN[CONF_TOKEN] @@ -448,8 +420,13 @@ async def test_token_auth_invalid_response(hass: HomeAssistant) -> None: assert result["data"][CONF_AUTH_TYPE] == AUTH_API_TOKEN -async def test_token_auth_single_plant(hass: HomeAssistant) -> None: +async def test_token_auth_single_plant( + hass: HomeAssistant, mock_growatt_v1_api, mock_setup_entry +) -> None: """Test token authentication with single plant.""" + # Repatch plant_list with full plant data for config flow + mock_growatt_v1_api.plant_list.return_value = GROWATT_V1_PLANT_LIST_RESPONSE + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -459,19 +436,9 @@ async def test_token_auth_single_plant(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "token_auth"} ) - with ( - patch( - "growattServer.OpenApiV1.plant_list", - return_value=GROWATT_V1_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_TOKEN] == FIXTURE_USER_INPUT_TOKEN[CONF_TOKEN] @@ -481,8 +448,13 @@ async def test_token_auth_single_plant(hass: HomeAssistant) -> None: assert result["result"].unique_id == "123456" -async def test_token_auth_multiple_plants(hass: HomeAssistant) -> None: +async def test_token_auth_multiple_plants( + hass: HomeAssistant, mock_growatt_v1_api, mock_setup_entry +) -> None: """Test token authentication with multiple plants.""" + # Repatch plant_list with multiple plants + mock_growatt_v1_api.plant_list.return_value = GROWATT_V1_MULTIPLE_PLANTS_RESPONSE + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -492,30 +464,20 @@ async def test_token_auth_multiple_plants(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "token_auth"} ) - with ( - patch( - "growattServer.OpenApiV1.plant_list", - return_value=GROWATT_V1_MULTIPLE_PLANTS_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) - - # Should show plant selection form - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "plant" - - # Select second plant - user_input = {CONF_PLANT_ID: "789012"} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) + + # Should show plant selection form + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "plant" + + # Select second plant + user_input = {CONF_PLANT_ID: "789012"} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input + ) + await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_TOKEN] == FIXTURE_USER_INPUT_TOKEN[CONF_TOKEN] @@ -525,10 +487,17 @@ async def test_token_auth_multiple_plants(hass: HomeAssistant) -> None: assert result["result"].unique_id == "789012" -async def test_password_auth_existing_plant_configured(hass: HomeAssistant) -> None: +async def test_password_auth_existing_plant_configured( + hass: HomeAssistant, + mock_growatt_classic_api, + mock_config_entry_classic: MockConfigEntry, +) -> None: """Test password authentication with existing plant_id.""" - entry = MockConfigEntry(domain=DOMAIN, unique_id="123456") - entry.add_to_hass(hass) + # Repatch plant_list for this test + mock_growatt_classic_api.plant_list.return_value = GROWATT_PLANT_LIST_RESPONSE + + # Use existing fixture (unique_id already matches what config flow returns) + mock_config_entry_classic.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -539,25 +508,23 @@ async def test_password_auth_existing_plant_configured(hass: HomeAssistant) -> N result["flow_id"], {"next_step_id": "password_auth"} ) - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - return_value=GROWATT_PLANT_LIST_RESPONSE, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" -async def test_token_auth_existing_plant_configured(hass: HomeAssistant) -> None: +async def test_token_auth_existing_plant_configured( + hass: HomeAssistant, mock_growatt_v1_api, mock_config_entry: MockConfigEntry +) -> None: """Test token authentication with existing plant_id.""" - entry = MockConfigEntry(domain=DOMAIN, unique_id="123456") - entry.add_to_hass(hass) + # Repatch plant_list for this test + mock_growatt_v1_api.plant_list.return_value = GROWATT_V1_PLANT_LIST_RESPONSE + + # Use existing fixture (unique_id already matches what config flow returns) + mock_config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -568,20 +535,23 @@ async def test_token_auth_existing_plant_configured(hass: HomeAssistant) -> None result["flow_id"], {"next_step_id": "token_auth"} ) - with patch( - "growattServer.OpenApiV1.plant_list", - return_value=GROWATT_V1_PLANT_LIST_RESPONSE, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_TOKEN - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_TOKEN + ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" -async def test_password_auth_connection_error(hass: HomeAssistant) -> None: +async def test_password_auth_connection_error( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with connection error, then recovery.""" + # Simulate connection error on first attempt + mock_growatt_classic_api.login.side_effect = requests.exceptions.ConnectionError( + "Connection failed" + ) + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -591,33 +561,22 @@ async def test_password_auth_connection_error(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "password_auth"} ) - with patch( - "growattServer.GrowattApi.login", - side_effect=requests.exceptions.ConnectionError("Connection failed"), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "password_auth" assert result["errors"] == {"base": ERROR_CANNOT_CONNECT} - # Test recovery - retry when connection is available - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - return_value=GROWATT_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + # Test recovery - reset side_effect and set normal return values + mock_growatt_classic_api.login.side_effect = None + mock_growatt_classic_api.login.return_value = GROWATT_LOGIN_RESPONSE + mock_growatt_classic_api.plant_list.return_value = GROWATT_PLANT_LIST_RESPONSE + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT_PASSWORD[CONF_USERNAME] @@ -626,7 +585,9 @@ async def test_password_auth_connection_error(hass: HomeAssistant) -> None: assert result["data"][CONF_AUTH_TYPE] == AUTH_PASSWORD -async def test_password_auth_invalid_response(hass: HomeAssistant) -> None: +async def test_password_auth_invalid_response( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with invalid response format, then recovery.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -637,33 +598,25 @@ async def test_password_auth_invalid_response(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "password_auth"} ) - with patch( - "growattServer.GrowattApi.login", - side_effect=ValueError("Invalid JSON response"), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + # Simulate invalid response error on first attempt + mock_growatt_classic_api.login.side_effect = ValueError("Invalid JSON response") + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "password_auth" assert result["errors"] == {"base": ERROR_CANNOT_CONNECT} - # Test recovery - retry with valid response - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - return_value=GROWATT_PLANT_LIST_RESPONSE, - ), - patch( - "homeassistant.components.growatt_server.async_setup_entry", - return_value=True, - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + # Test recovery - reset side_effect and set normal return values + mock_growatt_classic_api.login.side_effect = None + mock_growatt_classic_api.login.return_value = GROWATT_LOGIN_RESPONSE + mock_growatt_classic_api.plant_list.return_value = GROWATT_PLANT_LIST_RESPONSE + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.CREATE_ENTRY assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT_PASSWORD[CONF_USERNAME] @@ -672,7 +625,9 @@ async def test_password_auth_invalid_response(hass: HomeAssistant) -> None: assert result["data"][CONF_AUTH_TYPE] == AUTH_PASSWORD -async def test_password_auth_plant_list_error(hass: HomeAssistant) -> None: +async def test_password_auth_plant_list_error( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with plant list connection error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -683,22 +638,23 @@ async def test_password_auth_plant_list_error(hass: HomeAssistant) -> None: result["flow_id"], {"next_step_id": "password_auth"} ) - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - side_effect=requests.exceptions.ConnectionError("Connection failed"), - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + # Login succeeds but plant_list fails + mock_growatt_classic_api.login.return_value = GROWATT_LOGIN_RESPONSE + mock_growatt_classic_api.plant_list.side_effect = ( + requests.exceptions.ConnectionError("Connection failed") + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == ERROR_CANNOT_CONNECT -async def test_password_auth_plant_list_invalid_format(hass: HomeAssistant) -> None: +async def test_password_auth_plant_list_invalid_format( + hass: HomeAssistant, mock_growatt_classic_api, mock_setup_entry +) -> None: """Test password authentication with invalid plant list format.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -709,16 +665,13 @@ async def test_password_auth_plant_list_invalid_format(hass: HomeAssistant) -> N result["flow_id"], {"next_step_id": "password_auth"} ) - with ( - patch("growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE), - patch( - "growattServer.GrowattApi.plant_list", - return_value={"invalid": "format"}, # Missing "data" key - ), - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], FIXTURE_USER_INPUT_PASSWORD - ) + # Login succeeds but plant_list returns invalid format (missing "data" key) + mock_growatt_classic_api.login.return_value = GROWATT_LOGIN_RESPONSE + mock_growatt_classic_api.plant_list.return_value = {"invalid": "format"} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], FIXTURE_USER_INPUT_PASSWORD + ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == ERROR_CANNOT_CONNECT diff --git a/tests/components/growatt_server/test_init.py b/tests/components/growatt_server/test_init.py index 0a2807d15a243b..7294b769361296 100644 --- a/tests/components/growatt_server/test_init.py +++ b/tests/components/growatt_server/test_init.py @@ -2,7 +2,6 @@ from datetime import timedelta import json -from unittest.mock import patch from freezegun.api import FrozenDateTimeFactory import growattServer @@ -126,62 +125,6 @@ async def test_classic_api_setup( assert device_entry == snapshot -@pytest.mark.usefixtures("init_integration") -async def test_unload_removes_listeners( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, -) -> None: - """Test that unloading removes all listeners.""" - # Get initial listener count - initial_listeners = len(hass.bus.async_listeners()) - - # Unload the integration - await hass.config_entries.async_unload(mock_config_entry.entry_id) - await hass.async_block_till_done() - - # Verify listeners were removed (should be same or less) - final_listeners = len(hass.bus.async_listeners()) - assert final_listeners <= initial_listeners - - -async def test_multiple_devices_discovered( - hass: HomeAssistant, - snapshot: SnapshotAssertion, - mock_growatt_v1_api, - mock_config_entry: MockConfigEntry, - device_registry: dr.DeviceRegistry, -) -> None: - """Test handling multiple devices from device_list.""" - # Reset and add multiple devices - mock_config_entry_new = MockConfigEntry( - domain=DOMAIN, - data=mock_config_entry.data, - unique_id="plant_456", - ) - - mock_growatt_v1_api.device_list.return_value = { - "devices": [ - {"device_sn": "MIN123456", "type": 7}, - {"device_sn": "MIN789012", "type": 7}, - ] - } - - with patch( - "homeassistant.components.growatt_server.coordinator.SCAN_INTERVAL", - timedelta(minutes=5), - ): - await setup_integration(hass, mock_config_entry_new) - - # Verify both devices were created - device1 = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) - device2 = device_registry.async_get_device(identifiers={(DOMAIN, "MIN789012")}) - - assert device1 is not None - assert device1 == snapshot(name="device_min123456") - assert device2 is not None - assert device2 == snapshot(name="device_min789012") - - async def test_migrate_legacy_api_token_config( hass: HomeAssistant, mock_growatt_v1_api, diff --git a/tests/components/growatt_server/test_number.py b/tests/components/growatt_server/test_number.py index ffc4bc45561bbc..0d06b0d8d5010f 100644 --- a/tests/components/growatt_server/test_number.py +++ b/tests/components/growatt_server/test_number.py @@ -14,10 +14,10 @@ DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE, ) -from homeassistant.const import STATE_UNKNOWN, EntityCategory, Platform +from homeassistant.const import STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers import entity_registry as er from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform @@ -45,28 +45,6 @@ async def test_number_entities( await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_set_number_value_success( - hass: HomeAssistant, - mock_growatt_v1_api, -) -> None: - """Test setting a number entity value successfully.""" - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - "entity_id": "number.min123456_battery_charge_power_limit", - ATTR_VALUE: 75, - }, - blocking=True, - ) - - # Verify API was called with correct parameters - mock_growatt_v1_api.min_write_parameter.assert_called_once_with( - "MIN123456", "charge_power", 75 - ) - - @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") async def test_set_number_value_api_error( hass: HomeAssistant, @@ -88,50 +66,6 @@ async def test_set_number_value_api_error( ) -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_number_entity_attributes( - hass: HomeAssistant, - entity_registry: er.EntityRegistry, -) -> None: - """Test number entity attributes.""" - # Check entity registry attributes - entity_entry = entity_registry.async_get( - "number.min123456_battery_charge_power_limit" - ) - assert entity_entry is not None - assert entity_entry.entity_category == EntityCategory.CONFIG - assert entity_entry.unique_id == "MIN123456_battery_charge_power_limit" - - # Check state attributes - state = hass.states.get("number.min123456_battery_charge_power_limit") - assert state is not None - assert state.attributes["min"] == 0 - assert state.attributes["max"] == 100 - assert state.attributes["step"] == 1 - assert state.attributes["unit_of_measurement"] == "%" - assert state.attributes["friendly_name"] == "MIN123456 Battery charge power limit" - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_number_device_registry( - device_registry: dr.DeviceRegistry, - entity_registry: er.EntityRegistry, - snapshot: SnapshotAssertion, -) -> None: - """Test that number entities are associated with the correct device.""" - # Get the device from device registry - device = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) - assert device is not None - assert device == snapshot - - # Verify number entity is associated with the device - entity_entry = entity_registry.async_get( - "number.min123456_battery_charge_power_limit" - ) - assert entity_entry is not None - assert entity_entry.device_id == device.id - - @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") async def test_all_number_entities_service_calls( hass: HomeAssistant, @@ -162,38 +96,6 @@ async def test_all_number_entities_service_calls( ) -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_number_boundary_values( - hass: HomeAssistant, - mock_growatt_v1_api, -) -> None: - """Test setting boundary values for number entities.""" - # Test minimum value - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - {"entity_id": "number.min123456_battery_charge_power_limit", ATTR_VALUE: 0}, - blocking=True, - ) - - mock_growatt_v1_api.min_write_parameter.assert_called_with( - "MIN123456", "charge_power", 0 - ) - - # Test maximum value - mock_growatt_v1_api.reset_mock() - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - {"entity_id": "number.min123456_battery_charge_power_limit", ATTR_VALUE: 100}, - blocking=True, - ) - - mock_growatt_v1_api.min_write_parameter.assert_called_with( - "MIN123456", "charge_power", 100 - ) - - @pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_number_missing_data( hass: HomeAssistant, diff --git a/tests/components/growatt_server/test_sensor.py b/tests/components/growatt_server/test_sensor.py index cc7b9e631a7629..a008aaedccc71e 100644 --- a/tests/components/growatt_server/test_sensor.py +++ b/tests/components/growatt_server/test_sensor.py @@ -116,18 +116,3 @@ async def test_total_sensors_classic_api( await snapshot_platform( hass, entity_registry, snapshot, mock_config_entry_classic.entry_id ) - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default") -async def test_sensor_entity_registry( - hass: HomeAssistant, - snapshot: SnapshotAssertion, - mock_growatt_v1_api, - mock_config_entry: MockConfigEntry, - entity_registry: er.EntityRegistry, -) -> None: - """Test sensor entities are properly registered.""" - with patch("homeassistant.components.growatt_server.PLATFORMS", [Platform.SENSOR]): - await setup_integration(hass, mock_config_entry) - - await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) diff --git a/tests/components/growatt_server/test_switch.py b/tests/components/growatt_server/test_switch.py index 5ddc36906fa4f8..99952d404edcd5 100644 --- a/tests/components/growatt_server/test_switch.py +++ b/tests/components/growatt_server/test_switch.py @@ -21,7 +21,7 @@ ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers import entity_registry as er from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform @@ -50,115 +50,56 @@ async def test_switch_entities( @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_turn_on_switch_success( +@pytest.mark.parametrize( + ("service", "expected_value"), + [ + (SERVICE_TURN_ON, 1), + (SERVICE_TURN_OFF, 0), + ], +) +async def test_switch_service_call_success( hass: HomeAssistant, mock_growatt_v1_api, + service: str, + expected_value: int, ) -> None: - """Test turning on a switch entity successfully.""" + """Test switch service calls successfully.""" await hass.services.async_call( SWITCH_DOMAIN, - SERVICE_TURN_ON, + service, {ATTR_ENTITY_ID: "switch.min123456_charge_from_grid"}, blocking=True, ) # Verify API was called with correct parameters mock_growatt_v1_api.min_write_parameter.assert_called_once_with( - "MIN123456", "ac_charge", 1 - ) - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_turn_off_switch_success( - hass: HomeAssistant, - mock_growatt_v1_api, -) -> None: - """Test turning off a switch entity successfully.""" - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - {"entity_id": "switch.min123456_charge_from_grid"}, - blocking=True, - ) - - # Verify API was called with correct parameters - mock_growatt_v1_api.min_write_parameter.assert_called_once_with( - "MIN123456", "ac_charge", 0 + "MIN123456", "ac_charge", expected_value ) @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_turn_on_switch_api_error( - hass: HomeAssistant, - mock_growatt_v1_api, -) -> None: - """Test handling API error when turning on switch.""" - # Mock API to raise error - mock_growatt_v1_api.min_write_parameter.side_effect = GrowattV1ApiError("API Error") - - with pytest.raises(HomeAssistantError, match="Error while setting switch state"): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - {"entity_id": "switch.min123456_charge_from_grid"}, - blocking=True, - ) - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_turn_off_switch_api_error( +@pytest.mark.parametrize( + "service", + [SERVICE_TURN_ON, SERVICE_TURN_OFF], +) +async def test_switch_service_call_api_error( hass: HomeAssistant, mock_growatt_v1_api, + service: str, ) -> None: - """Test handling API error when turning off switch.""" + """Test handling API error when calling switch services.""" # Mock API to raise error mock_growatt_v1_api.min_write_parameter.side_effect = GrowattV1ApiError("API Error") with pytest.raises(HomeAssistantError, match="Error while setting switch state"): await hass.services.async_call( SWITCH_DOMAIN, - SERVICE_TURN_OFF, + service, {"entity_id": "switch.min123456_charge_from_grid"}, blocking=True, ) -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_switch_entity_attributes( - hass: HomeAssistant, - snapshot: SnapshotAssertion, - entity_registry: er.EntityRegistry, -) -> None: - """Test switch entity attributes.""" - # Check entity registry attributes - entity_entry = entity_registry.async_get("switch.min123456_charge_from_grid") - assert entity_entry is not None - assert entity_entry == snapshot(name="entity_entry") - - # Check state attributes - state = hass.states.get("switch.min123456_charge_from_grid") - assert state is not None - assert state == snapshot(name="state") - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") -async def test_switch_device_registry( - device_registry: dr.DeviceRegistry, - entity_registry: er.EntityRegistry, - snapshot: SnapshotAssertion, -) -> None: - """Test that switch entities are associated with the correct device.""" - # Get the device from device registry - device = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) - assert device is not None - assert device == snapshot - - # Verify switch entity is associated with the device - entity_entry = entity_registry.async_get("switch.min123456_charge_from_grid") - assert entity_entry is not None - assert entity_entry.device_id == device.id - - @pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_switch_state_handling_integer_values( hass: HomeAssistant, diff --git a/tests/components/netatmo/common.py b/tests/components/netatmo/common.py index acdc3c491ff94b..617a0070f5c468 100644 --- a/tests/components/netatmo/common.py +++ b/tests/components/netatmo/common.py @@ -119,7 +119,7 @@ def selected_platforms(platforms: list[Platform]) -> Iterator[None]: with ( patch("homeassistant.components.netatmo.data_handler.PLATFORMS", platforms), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/netatmo/test_camera.py b/tests/components/netatmo/test_camera.py index 72b18f2e1d2550..042806b932427f 100644 --- a/tests/components/netatmo/test_camera.py +++ b/tests/components/netatmo/test_camera.py @@ -416,7 +416,7 @@ async def fake_post(*args: Any, **kwargs: Any): ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["camera"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -515,7 +515,7 @@ async def fake_post_no_data(*args, **kwargs): ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["camera"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -558,7 +558,7 @@ async def fake_post(*args: Any, **kwargs: Any): ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["camera"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/netatmo/test_diagnostics.py b/tests/components/netatmo/test_diagnostics.py index 1ada0bdd2bf23d..dde28fe3bc7ca4 100644 --- a/tests/components/netatmo/test_diagnostics.py +++ b/tests/components/netatmo/test_diagnostics.py @@ -28,7 +28,7 @@ async def test_entry_diagnostics( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/netatmo/test_init.py b/tests/components/netatmo/test_init.py index eb052b93288b70..540927fdb78a8e 100644 --- a/tests/components/netatmo/test_init.py +++ b/tests/components/netatmo/test_init.py @@ -16,6 +16,9 @@ from homeassistant.const import CONF_WEBHOOK_ID, Platform from homeassistant.core import CoreState, HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, +) from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util @@ -65,7 +68,7 @@ async def test_setup_component( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, ): @@ -108,7 +111,7 @@ async def fake_post(*args, **kwargs): with ( patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, patch( @@ -181,7 +184,7 @@ async def test_setup_without_https( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth" ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url" @@ -225,7 +228,7 @@ async def test_setup_with_cloud( ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", []), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -295,7 +298,7 @@ async def test_setup_with_cloudhook(hass: HomeAssistant) -> None: ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", []), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -340,7 +343,7 @@ async def test_setup_component_with_delay( "pyatmo.AbstractAsyncAuth.async_dropwebhook", side_effect=AsyncMock() ) as mock_dropwebhook, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, patch( @@ -410,7 +413,7 @@ async def test_setup_component_invalid_token_scope(hass: HomeAssistant) -> None: "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, ): @@ -459,12 +462,10 @@ async def fake_ensure_valid_token(*args, **kwargs): "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, - patch( - "homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session" - ) as mock_session, + patch("homeassistant.components.netatmo.OAuth2Session") as mock_session, ): mock_auth.return_value.async_post_api_request.side_effect = partial( fake_post_request, hass @@ -557,3 +558,19 @@ async def test_device_remove_devices( ) response = await client.remove_device(dead_device_entry.id, config_entry.entry_id) assert response["success"] + + +async def test_oauth_implementation_not_available( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test that unavailable OAuth implementation raises ConfigEntryNotReady.""" + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.netatmo.async_get_config_entry_implementation", + side_effect=ImplementationUnavailableError, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/components/netatmo/test_light.py b/tests/components/netatmo/test_light.py index 16a3ac2aaeb490..0ede4146044361 100644 --- a/tests/components/netatmo/test_light.py +++ b/tests/components/netatmo/test_light.py @@ -129,7 +129,7 @@ async def fake_post_request_no_data(*args, **kwargs): ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["light"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/tesla_fleet/test_init.py b/tests/components/tesla_fleet/test_init.py index 02f1ca940a6741..eabb00aafbdc64 100644 --- a/tests/components/tesla_fleet/test_init.py +++ b/tests/components/tesla_fleet/test_init.py @@ -35,6 +35,9 @@ from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, +) from . import setup_platform from .conftest import create_config_entry @@ -561,3 +564,20 @@ async def test_vehicle_with_location_scope( assert VehicleDataEndpoint.DRIVE_STATE in endpoints assert VehicleDataEndpoint.VEHICLE_STATE in endpoints assert VehicleDataEndpoint.VEHICLE_CONFIG in endpoints + + +async def test_oauth_implementation_not_available( + hass: HomeAssistant, + normal_config_entry: MockConfigEntry, +) -> None: + """Test that unavailable OAuth implementation raises ConfigEntryNotReady.""" + normal_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.tesla_fleet.async_get_config_entry_implementation", + side_effect=ImplementationUnavailableError, + ): + await hass.config_entries.async_setup(normal_config_entry.entry_id) + await hass.async_block_till_done() + + assert normal_config_entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/components/toon/test_init.py b/tests/components/toon/test_init.py new file mode 100644 index 00000000000000..02efc3095a1911 --- /dev/null +++ b/tests/components/toon/test_init.py @@ -0,0 +1,42 @@ +"""Tests for the Toon component.""" + +from unittest.mock import patch + +from homeassistant.components.toon import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, +) + +from tests.common import MockConfigEntry + + +async def test_oauth_implementation_not_available( + hass: HomeAssistant, +) -> None: + """Test that unavailable OAuth implementation raises ConfigEntryNotReady.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + version=2, + data={ + "auth_implementation": DOMAIN, + "token": { + "refresh_token": "mock-refresh-token", + "access_token": "mock-access-token", + "type": "Bearer", + "expires_in": 60, + }, + "agreement_id": "test-agreement-id", + }, + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.toon.async_get_config_entry_implementation", + side_effect=ImplementationUnavailableError, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/helpers/template/extensions/test_base.py b/tests/helpers/template/extensions/test_base.py new file mode 100644 index 00000000000000..ce2bf966e6b990 --- /dev/null +++ b/tests/helpers/template/extensions/test_base.py @@ -0,0 +1,285 @@ +"""Test base template extension.""" + +from __future__ import annotations + +import pytest + +from homeassistant.helpers.template import TemplateEnvironment +from homeassistant.helpers.template.extensions.base import ( + BaseTemplateExtension, + TemplateFunction, +) + + +def test_hass_property_raises_when_hass_is_none() -> None: + """Test that accessing hass property raises RuntimeError when hass is None.""" + # Create an environment without hass + env = TemplateEnvironment(None) + + # Create a simple extension + extension = BaseTemplateExtension(env) + + # Accessing hass property should raise RuntimeError + with pytest.raises( + RuntimeError, + match=( + "Home Assistant instance is not available. " + "This property should only be used in extensions with " + "functions marked requires_hass=True." + ), + ): + _ = extension.hass + + +def test_requires_hass_functions_not_registered_without_hass() -> None: + """Test that functions requiring hass are not registered when hass is None.""" + # Create an environment without hass + env = TemplateEnvironment(None) + + # Create a test function + def test_func() -> str: + return "test" + + # Create extension with a function that requires hass + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "test_func", + test_func, + as_global=True, + requires_hass=True, + ), + ], + ) + + # Function should not be registered + assert "test_func" not in env.globals + assert extension is not None # Extension is created but function not registered + + +def test_requires_hass_false_functions_registered_without_hass() -> None: + """Test that functions not requiring hass are registered even when hass is None.""" + # Create an environment without hass + env = TemplateEnvironment(None) + + # Create a test function + def test_func() -> str: + return "test" + + # Create extension with a function that does not require hass + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "test_func", + test_func, + as_global=True, + requires_hass=False, # Explicitly False (default) + ), + ], + ) + + # Function should be registered + assert "test_func" in env.globals + assert extension is not None + + +def test_limited_ok_functions_not_registered_in_limited_env() -> None: + """Test that functions with limited_ok=False are not registered in limited env.""" + # Create a limited environment without hass + env = TemplateEnvironment(None, limited=True) + + # Create test functions + def allowed_func() -> str: + return "allowed" + + def restricted_func() -> str: + return "restricted" + + # Create extension with both types of functions + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "allowed_func", + allowed_func, + as_global=True, + limited_ok=True, # Allowed in limited environments + ), + TemplateFunction( + "restricted_func", + restricted_func, + as_global=True, + limited_ok=False, # Not allowed in limited environments + ), + ], + ) + + # Only the allowed function should be registered + assert "allowed_func" in env.globals + assert "restricted_func" not in env.globals + assert extension is not None + + +def test_limited_ok_true_functions_registered_in_limited_env() -> None: + """Test that functions with limited_ok=True are registered in limited env.""" + # Create a limited environment without hass + env = TemplateEnvironment(None, limited=True) + + # Create a test function + def test_func() -> str: + return "test" + + # Create extension with a function allowed in limited environments + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "test_func", + test_func, + as_global=True, + limited_ok=True, # Default is True + ), + ], + ) + + # Function should be registered + assert "test_func" in env.globals + assert extension is not None + + +def test_function_registered_as_global() -> None: + """Test that functions can be registered as globals.""" + env = TemplateEnvironment(None) + + def test_func() -> str: + return "global" + + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "test_func", + test_func, + as_global=True, + ), + ], + ) + + # Function should be registered as a global + assert "test_func" in env.globals + assert env.globals["test_func"] is test_func + assert extension is not None + + +def test_function_registered_as_filter() -> None: + """Test that functions can be registered as filters.""" + env = TemplateEnvironment(None) + + def test_filter(value: str) -> str: + return f"filtered_{value}" + + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "test_filter", + test_filter, + as_filter=True, + ), + ], + ) + + # Function should be registered as a filter + assert "test_filter" in env.filters + assert env.filters["test_filter"] is test_filter + # Should not be in globals since as_global=False + assert "test_filter" not in env.globals + assert extension is not None + + +def test_function_registered_as_test() -> None: + """Test that functions can be registered as tests.""" + env = TemplateEnvironment(None) + + def test_check(value: str) -> bool: + return value == "test" + + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "test_check", + test_check, + as_test=True, + ), + ], + ) + + # Function should be registered as a test + assert "test_check" in env.tests + assert env.tests["test_check"] is test_check + # Should not be in globals or filters + assert "test_check" not in env.globals + assert "test_check" not in env.filters + assert extension is not None + + +def test_function_registered_as_multiple_types() -> None: + """Test that functions can be registered as multiple types simultaneously.""" + env = TemplateEnvironment(None) + + def multi_func(value: str = "default") -> str: + return f"multi_{value}" + + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction( + "multi_func", + multi_func, + as_global=True, + as_filter=True, + as_test=True, + ), + ], + ) + + # Function should be registered in all three places + assert "multi_func" in env.globals + assert env.globals["multi_func"] is multi_func + assert "multi_func" in env.filters + assert env.filters["multi_func"] is multi_func + assert "multi_func" in env.tests + assert env.tests["multi_func"] is multi_func + assert extension is not None + + +def test_multiple_functions_registered() -> None: + """Test that multiple functions can be registered at once.""" + env = TemplateEnvironment(None) + + def func1() -> str: + return "one" + + def func2() -> str: + return "two" + + def func3() -> str: + return "three" + + extension = BaseTemplateExtension( + env, + functions=[ + TemplateFunction("func1", func1, as_global=True), + TemplateFunction("func2", func2, as_filter=True), + TemplateFunction("func3", func3, as_test=True), + ], + ) + + # All functions should be registered in their respective places + assert "func1" in env.globals + assert "func2" in env.filters + assert "func3" in env.tests + assert extension is not None