diff --git a/build.yaml b/build.yaml index e82fd192123b2..4994cdafcd707 100644 --- a/build.yaml +++ b/build.yaml @@ -1,10 +1,10 @@ image: ghcr.io/home-assistant/{arch}-homeassistant build_from: - aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.10.1 - armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.10.1 - armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.10.1 - amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.10.1 - i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.10.1 + aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.11.0 + armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.11.0 + armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.11.0 + amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.11.0 + i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.11.0 cosign: base_identity: https://github.com/home-assistant/docker/.* identity: https://github.com/home-assistant/core/.* diff --git a/homeassistant/components/asuswrt/bridge.py b/homeassistant/components/asuswrt/bridge.py index 81ace7d2873fc..a1bfad989d4a3 100644 --- a/homeassistant/components/asuswrt/bridge.py +++ b/homeassistant/components/asuswrt/bridge.py @@ -111,8 +111,6 @@ async def _wrapper( if isinstance(data, dict): return dict(zip(keys, list(data.values()), strict=False)) - if not isinstance(data, (list, tuple)): - raise UpdateFailed("Received invalid data type") return dict(zip(keys, data, strict=False)) return _wrapper diff --git a/homeassistant/components/bsblan/climate.py b/homeassistant/components/bsblan/climate.py index ff8b06447d4ec..f13d88278b436 100644 --- a/homeassistant/components/bsblan/climate.py +++ b/homeassistant/components/bsblan/climate.py @@ -74,8 +74,11 @@ def __init__( super().__init__(data.fast_coordinator, data) self._attr_unique_id = f"{format_mac(data.device.MAC)}-climate" - self._attr_min_temp = data.static.min_temp.value - self._attr_max_temp = data.static.max_temp.value + # Set temperature range if available, otherwise use Home Assistant defaults + if data.static.min_temp is not None and data.static.min_temp.value is not None: + self._attr_min_temp = data.static.min_temp.value + if data.static.max_temp is not None and data.static.max_temp.value is not None: + self._attr_max_temp = data.static.max_temp.value self._attr_temperature_unit = data.fast_coordinator.client.get_temperature_unit @property diff --git a/homeassistant/components/bsblan/manifest.json b/homeassistant/components/bsblan/manifest.json index ce82f89441c28..931020e0dca63 100644 --- a/homeassistant/components/bsblan/manifest.json +++ b/homeassistant/components/bsblan/manifest.json @@ -7,7 +7,7 @@ "integration_type": "device", "iot_class": "local_polling", "loggers": ["bsblan"], - "requirements": ["python-bsblan==3.1.0"], + "requirements": ["python-bsblan==3.1.1"], "zeroconf": [ { "name": "bsb-lan*", diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index c11680e487fd1..05e420cb4143a 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -55,6 +55,7 @@ CONF_ALIASES, CONF_API_SERVER, CONF_COGNITO_CLIENT_ID, + CONF_DISCOVERY_SERVICE_ACTIONS, CONF_ENTITY_CONFIG, CONF_FILTER, CONF_GOOGLE_ACTIONS, @@ -139,6 +140,7 @@ { vol.Required(CONF_MODE): vol.In([MODE_DEV]), vol.Required(CONF_API_SERVER): str, + vol.Optional(CONF_DISCOVERY_SERVICE_ACTIONS): {str: cv.url}, } ), _BASE_CONFIG_SCHEMA.extend( diff --git a/homeassistant/components/cloud/const.py b/homeassistant/components/cloud/const.py index 29b843107ff53..ab3ce2365dee8 100644 --- a/homeassistant/components/cloud/const.py +++ b/homeassistant/components/cloud/const.py @@ -79,6 +79,7 @@ CONF_ACCOUNTS_SERVER = "accounts_server" CONF_ACME_SERVER = "acme_server" CONF_API_SERVER = "api_server" +CONF_DISCOVERY_SERVICE_ACTIONS = "discovery_service_actions" CONF_RELAYER_SERVER = "relayer_server" CONF_REMOTESTATE_SERVER = "remotestate_server" CONF_SERVICEHANDLERS_SERVER = "servicehandlers_server" diff --git a/homeassistant/components/emoncms/quality_scale.yaml b/homeassistant/components/emoncms/quality_scale.yaml new file mode 100644 index 0000000000000..ae2edd18a2370 --- /dev/null +++ b/homeassistant/components/emoncms/quality_scale.yaml @@ -0,0 +1,84 @@ +rules: + # todo : add get_feed_list to the library + # todo : see if we can drop some extra attributes + # Bronze + action-setup: + status: exempt + comment: | + This integration does not provide additional actions. + appropriate-polling: done + brands: done + common-modules: done + config-flow-test-coverage: + status: todo + comment: | + test_reconfigure_api_error should use a mock config entry fixture + test_user_flow_failure should use a mock config entry fixture + move test_user_flow_* to the top of the file + config-flow: done + dependency-transparency: done + docs-actions: + status: exempt + comment: | + This integration does not provide additional actions. + docs-high-level-description: done + docs-installation-instructions: done + docs-removal-instructions: done + entity-event-setup: + status: exempt + comment: | + No events are explicitly registered by the integration. + entity-unique-id: done + has-entity-name: done + runtime-data: done + test-before-configure: done + test-before-setup: done + unique-config-entry: done + + # Silver + action-exceptions: done + config-entry-unloading: done + docs-configuration-parameters: done + docs-installation-parameters: done + entity-unavailable: todo + integration-owner: done + log-when-unavailable: done + parallel-updates: todo + reauthentication-flow: todo + test-coverage: + status: todo + comment: | + test the entry state in test_failure + + # Gold + devices: todo + diagnostics: todo + discovery-update-info: todo + discovery: todo + docs-data-update: done + docs-examples: + status: exempt + comment: | + This integration does not provide any automation + docs-known-limitations: todo + docs-supported-devices: todo + docs-supported-functions: done + docs-troubleshooting: done + docs-use-cases: todo + dynamic-devices: todo + entity-category: todo + entity-device-class: + status: todo + comment: change device_class=SensorDeviceClass.SIGNAL_STRENGTH to SOUND_PRESSURE + entity-disabled-by-default: todo + entity-translations: done + exception-translations: todo + icon-translations: todo + reconfiguration-flow: done + repair-issues: todo + stale-devices: todo + + # Platinum + async-dependency: done + inject-websession: done + strict-typing: todo diff --git a/homeassistant/components/energy/validate.py b/homeassistant/components/energy/validate.py index 62ff7beac5e8f..8fe188985fee4 100644 --- a/homeassistant/components/energy/validate.py +++ b/homeassistant/components/energy/validate.py @@ -386,247 +386,301 @@ def _async_validate_auto_generated_cost_entity( issues.add_issue(hass, "recorder_untracked", cost_entity_id) -async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation: - """Validate the energy configuration.""" - manager: data.EnergyManager = await data.async_get_manager(hass) - statistics_metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]] = {} - validate_calls = [] - wanted_statistics_metadata: set[str] = set() - - result = EnergyPreferencesValidation() - - if manager.data is None: - return result - - # Create a list of validation checks - for source in manager.data["energy_sources"]: - source_result = ValidationIssues() - result.energy_sources.append(source_result) +def _validate_grid_source( + hass: HomeAssistant, + source: data.GridSourceType, + statistics_metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]], + wanted_statistics_metadata: set[str], + source_result: ValidationIssues, + validate_calls: list[functools.partial[None]], +) -> None: + """Validate grid energy source.""" + flow_from: data.FlowFromGridSourceType + for flow_from in source["flow_from"]: + wanted_statistics_metadata.add(flow_from["stat_energy_from"]) + validate_calls.append( + functools.partial( + _async_validate_usage_stat, + hass, + statistics_metadata, + flow_from["stat_energy_from"], + ENERGY_USAGE_DEVICE_CLASSES, + ENERGY_USAGE_UNITS, + ENERGY_UNIT_ERROR, + source_result, + ) + ) - if source["type"] == "grid": - flow: data.FlowFromGridSourceType | data.FlowToGridSourceType - for flow in source["flow_from"]: - wanted_statistics_metadata.add(flow["stat_energy_from"]) - validate_calls.append( - functools.partial( - _async_validate_usage_stat, - hass, - statistics_metadata, - flow["stat_energy_from"], - ENERGY_USAGE_DEVICE_CLASSES, - ENERGY_USAGE_UNITS, - ENERGY_UNIT_ERROR, - source_result, - ) + if (stat_cost := flow_from.get("stat_cost")) is not None: + wanted_statistics_metadata.add(stat_cost) + validate_calls.append( + functools.partial( + _async_validate_cost_stat, + hass, + statistics_metadata, + stat_cost, + source_result, ) - - if (stat_cost := flow.get("stat_cost")) is not None: - wanted_statistics_metadata.add(stat_cost) - validate_calls.append( - functools.partial( - _async_validate_cost_stat, - hass, - statistics_metadata, - stat_cost, - source_result, - ) - ) - elif ( - entity_energy_price := flow.get("entity_energy_price") - ) is not None: - validate_calls.append( - functools.partial( - _async_validate_price_entity, - hass, - entity_energy_price, - source_result, - ENERGY_PRICE_UNITS, - ENERGY_PRICE_UNIT_ERROR, - ) - ) - - if ( - flow.get("entity_energy_price") is not None - or flow.get("number_energy_price") is not None - ): - validate_calls.append( - functools.partial( - _async_validate_auto_generated_cost_entity, - hass, - flow["stat_energy_from"], - source_result, - ) - ) - - for flow in source["flow_to"]: - wanted_statistics_metadata.add(flow["stat_energy_to"]) - validate_calls.append( - functools.partial( - _async_validate_usage_stat, - hass, - statistics_metadata, - flow["stat_energy_to"], - ENERGY_USAGE_DEVICE_CLASSES, - ENERGY_USAGE_UNITS, - ENERGY_UNIT_ERROR, - source_result, - ) + ) + elif (entity_energy_price := flow_from.get("entity_energy_price")) is not None: + validate_calls.append( + functools.partial( + _async_validate_price_entity, + hass, + entity_energy_price, + source_result, + ENERGY_PRICE_UNITS, + ENERGY_PRICE_UNIT_ERROR, ) + ) - if (stat_compensation := flow.get("stat_compensation")) is not None: - wanted_statistics_metadata.add(stat_compensation) - validate_calls.append( - functools.partial( - _async_validate_cost_stat, - hass, - statistics_metadata, - stat_compensation, - source_result, - ) - ) - elif ( - entity_energy_price := flow.get("entity_energy_price") - ) is not None: - validate_calls.append( - functools.partial( - _async_validate_price_entity, - hass, - entity_energy_price, - source_result, - ENERGY_PRICE_UNITS, - ENERGY_PRICE_UNIT_ERROR, - ) - ) - - if ( - flow.get("entity_energy_price") is not None - or flow.get("number_energy_price") is not None - ): - validate_calls.append( - functools.partial( - _async_validate_auto_generated_cost_entity, - hass, - flow["stat_energy_to"], - source_result, - ) - ) - - for power_stat in source.get("power", []): - wanted_statistics_metadata.add(power_stat["stat_rate"]) - validate_calls.append( - functools.partial( - _async_validate_power_stat, - hass, - statistics_metadata, - power_stat["stat_rate"], - POWER_USAGE_DEVICE_CLASSES, - POWER_USAGE_UNITS, - POWER_UNIT_ERROR, - source_result, - ) + if ( + flow_from.get("entity_energy_price") is not None + or flow_from.get("number_energy_price") is not None + ): + validate_calls.append( + functools.partial( + _async_validate_auto_generated_cost_entity, + hass, + flow_from["stat_energy_from"], + source_result, ) + ) - elif source["type"] == "gas": - wanted_statistics_metadata.add(source["stat_energy_from"]) + flow_to: data.FlowToGridSourceType + for flow_to in source["flow_to"]: + wanted_statistics_metadata.add(flow_to["stat_energy_to"]) + validate_calls.append( + functools.partial( + _async_validate_usage_stat, + hass, + statistics_metadata, + flow_to["stat_energy_to"], + ENERGY_USAGE_DEVICE_CLASSES, + ENERGY_USAGE_UNITS, + ENERGY_UNIT_ERROR, + source_result, + ) + ) + + if (stat_compensation := flow_to.get("stat_compensation")) is not None: + wanted_statistics_metadata.add(stat_compensation) validate_calls.append( functools.partial( - _async_validate_usage_stat, + _async_validate_cost_stat, hass, statistics_metadata, - source["stat_energy_from"], - GAS_USAGE_DEVICE_CLASSES, - GAS_USAGE_UNITS, - GAS_UNIT_ERROR, + stat_compensation, source_result, ) ) - - if (stat_cost := source.get("stat_cost")) is not None: - wanted_statistics_metadata.add(stat_cost) - validate_calls.append( - functools.partial( - _async_validate_cost_stat, - hass, - statistics_metadata, - stat_cost, - source_result, - ) - ) - elif (entity_energy_price := source.get("entity_energy_price")) is not None: - validate_calls.append( - functools.partial( - _async_validate_price_entity, - hass, - entity_energy_price, - source_result, - GAS_PRICE_UNITS, - GAS_PRICE_UNIT_ERROR, - ) - ) - - if ( - source.get("entity_energy_price") is not None - or source.get("number_energy_price") is not None - ): - validate_calls.append( - functools.partial( - _async_validate_auto_generated_cost_entity, - hass, - source["stat_energy_from"], - source_result, - ) + elif (entity_energy_price := flow_to.get("entity_energy_price")) is not None: + validate_calls.append( + functools.partial( + _async_validate_price_entity, + hass, + entity_energy_price, + source_result, + ENERGY_PRICE_UNITS, + ENERGY_PRICE_UNIT_ERROR, ) + ) - elif source["type"] == "water": - wanted_statistics_metadata.add(source["stat_energy_from"]) + if ( + flow_to.get("entity_energy_price") is not None + or flow_to.get("number_energy_price") is not None + ): validate_calls.append( functools.partial( - _async_validate_usage_stat, + _async_validate_auto_generated_cost_entity, hass, - statistics_metadata, - source["stat_energy_from"], - WATER_USAGE_DEVICE_CLASSES, - WATER_USAGE_UNITS, - WATER_UNIT_ERROR, + flow_to["stat_energy_to"], source_result, ) ) - if (stat_cost := source.get("stat_cost")) is not None: - wanted_statistics_metadata.add(stat_cost) - validate_calls.append( - functools.partial( - _async_validate_cost_stat, - hass, - statistics_metadata, - stat_cost, - source_result, - ) - ) - elif (entity_energy_price := source.get("entity_energy_price")) is not None: - validate_calls.append( - functools.partial( - _async_validate_price_entity, - hass, - entity_energy_price, - source_result, - WATER_PRICE_UNITS, - WATER_PRICE_UNIT_ERROR, - ) - ) + for power_stat in source.get("power", []): + wanted_statistics_metadata.add(power_stat["stat_rate"]) + validate_calls.append( + functools.partial( + _async_validate_power_stat, + hass, + statistics_metadata, + power_stat["stat_rate"], + POWER_USAGE_DEVICE_CLASSES, + POWER_USAGE_UNITS, + POWER_UNIT_ERROR, + source_result, + ) + ) - if ( - source.get("entity_energy_price") is not None - or source.get("number_energy_price") is not None - ): - validate_calls.append( - functools.partial( - _async_validate_auto_generated_cost_entity, - hass, - source["stat_energy_from"], - source_result, - ) - ) + +def _validate_gas_source( + hass: HomeAssistant, + source: data.GasSourceType, + statistics_metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]], + wanted_statistics_metadata: set[str], + source_result: ValidationIssues, + validate_calls: list[functools.partial[None]], +) -> None: + """Validate gas energy source.""" + wanted_statistics_metadata.add(source["stat_energy_from"]) + validate_calls.append( + functools.partial( + _async_validate_usage_stat, + hass, + statistics_metadata, + source["stat_energy_from"], + GAS_USAGE_DEVICE_CLASSES, + GAS_USAGE_UNITS, + GAS_UNIT_ERROR, + source_result, + ) + ) + + if (stat_cost := source.get("stat_cost")) is not None: + wanted_statistics_metadata.add(stat_cost) + validate_calls.append( + functools.partial( + _async_validate_cost_stat, + hass, + statistics_metadata, + stat_cost, + source_result, + ) + ) + elif (entity_energy_price := source.get("entity_energy_price")) is not None: + validate_calls.append( + functools.partial( + _async_validate_price_entity, + hass, + entity_energy_price, + source_result, + GAS_PRICE_UNITS, + GAS_PRICE_UNIT_ERROR, + ) + ) + + if ( + source.get("entity_energy_price") is not None + or source.get("number_energy_price") is not None + ): + validate_calls.append( + functools.partial( + _async_validate_auto_generated_cost_entity, + hass, + source["stat_energy_from"], + source_result, + ) + ) + + +def _validate_water_source( + hass: HomeAssistant, + source: data.WaterSourceType, + statistics_metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]], + wanted_statistics_metadata: set[str], + source_result: ValidationIssues, + validate_calls: list[functools.partial[None]], +) -> None: + """Validate water energy source.""" + wanted_statistics_metadata.add(source["stat_energy_from"]) + validate_calls.append( + functools.partial( + _async_validate_usage_stat, + hass, + statistics_metadata, + source["stat_energy_from"], + WATER_USAGE_DEVICE_CLASSES, + WATER_USAGE_UNITS, + WATER_UNIT_ERROR, + source_result, + ) + ) + + if (stat_cost := source.get("stat_cost")) is not None: + wanted_statistics_metadata.add(stat_cost) + validate_calls.append( + functools.partial( + _async_validate_cost_stat, + hass, + statistics_metadata, + stat_cost, + source_result, + ) + ) + elif (entity_energy_price := source.get("entity_energy_price")) is not None: + validate_calls.append( + functools.partial( + _async_validate_price_entity, + hass, + entity_energy_price, + source_result, + WATER_PRICE_UNITS, + WATER_PRICE_UNIT_ERROR, + ) + ) + + if ( + source.get("entity_energy_price") is not None + or source.get("number_energy_price") is not None + ): + validate_calls.append( + functools.partial( + _async_validate_auto_generated_cost_entity, + hass, + source["stat_energy_from"], + source_result, + ) + ) + + +async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation: + """Validate the energy configuration.""" + manager: data.EnergyManager = await data.async_get_manager(hass) + statistics_metadata: dict[str, tuple[int, recorder.models.StatisticMetaData]] = {} + validate_calls: list[functools.partial[None]] = [] + wanted_statistics_metadata: set[str] = set() + + result = EnergyPreferencesValidation() + + if manager.data is None: + return result + + # Create a list of validation checks + for source in manager.data["energy_sources"]: + source_result = ValidationIssues() + result.energy_sources.append(source_result) + + if source["type"] == "grid": + _validate_grid_source( + hass, + source, + statistics_metadata, + wanted_statistics_metadata, + source_result, + validate_calls, + ) + + elif source["type"] == "gas": + _validate_gas_source( + hass, + source, + statistics_metadata, + wanted_statistics_metadata, + source_result, + validate_calls, + ) + + elif source["type"] == "water": + _validate_water_source( + hass, + source, + statistics_metadata, + wanted_statistics_metadata, + source_result, + validate_calls, + ) elif source["type"] == "solar": wanted_statistics_metadata.add(source["stat_energy_from"]) diff --git a/homeassistant/components/enphase_envoy/diagnostics.py b/homeassistant/components/enphase_envoy/diagnostics.py index 6487830675fc6..e3d86e7fa233c 100644 --- a/homeassistant/components/enphase_envoy/diagnostics.py +++ b/homeassistant/components/enphase_envoy/diagnostics.py @@ -147,6 +147,8 @@ async def async_get_config_entry_diagnostics( "ctmeter_production_phases": envoy_data.ctmeter_production_phases, "ctmeter_consumption_phases": envoy_data.ctmeter_consumption_phases, "ctmeter_storage_phases": envoy_data.ctmeter_storage_phases, + "ctmeters": envoy_data.ctmeters, + "ctmeters_phases": envoy_data.ctmeters_phases, "dry_contact_status": envoy_data.dry_contact_status, "dry_contact_settings": envoy_data.dry_contact_settings, "inverters": envoy_data.inverters, @@ -179,6 +181,7 @@ async def async_get_config_entry_diagnostics( "ct_consumption_meter": envoy.consumption_meter_type, "ct_production_meter": envoy.production_meter_type, "ct_storage_meter": envoy.storage_meter_type, + "ct_meters": list(envoy_data.ctmeters.keys()), } fixture_data: dict[str, Any] = {} diff --git a/homeassistant/components/enphase_envoy/sensor.py b/homeassistant/components/enphase_envoy/sensor.py index 807798c48cf7c..73f249ec1bd8d 100644 --- a/homeassistant/components/enphase_envoy/sensor.py +++ b/homeassistant/components/enphase_envoy/sensor.py @@ -399,330 +399,189 @@ class EnvoyCTSensorEntityDescription(SensorEntityDescription): cttype: str | None = None -CT_NET_CONSUMPTION_SENSORS = ( - EnvoyCTSensorEntityDescription( - key="lifetime_net_consumption", - translation_key="lifetime_net_consumption", - native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, - suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, - suggested_display_precision=3, - value_fn=attrgetter("energy_delivered"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="lifetime_net_production", - translation_key="lifetime_net_production", - native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, - suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, - suggested_display_precision=3, - value_fn=attrgetter("energy_received"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="net_consumption", - translation_key="net_consumption", - native_unit_of_measurement=UnitOfPower.WATT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.POWER, - suggested_unit_of_measurement=UnitOfPower.KILO_WATT, - suggested_display_precision=3, - value_fn=attrgetter("active_power"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="frequency", - translation_key="net_ct_frequency", - native_unit_of_measurement=UnitOfFrequency.HERTZ, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.FREQUENCY, - suggested_display_precision=1, - entity_registry_enabled_default=False, - value_fn=attrgetter("frequency"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="voltage", - translation_key="net_ct_voltage", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.VOLTAGE, - suggested_unit_of_measurement=UnitOfElectricPotential.VOLT, - suggested_display_precision=1, - entity_registry_enabled_default=False, - value_fn=attrgetter("voltage"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="net_ct_current", - translation_key="net_ct_current", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.CURRENT, - suggested_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - suggested_display_precision=3, - entity_registry_enabled_default=False, - value_fn=attrgetter("current"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="net_ct_powerfactor", - translation_key="net_ct_powerfactor", - device_class=SensorDeviceClass.POWER_FACTOR, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=2, - entity_registry_enabled_default=False, - value_fn=attrgetter("power_factor"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="net_consumption_ct_metering_status", - translation_key="net_ct_metering_status", - device_class=SensorDeviceClass.ENUM, - entity_category=EntityCategory.DIAGNOSTIC, - options=list(CtMeterStatus), - entity_registry_enabled_default=False, - value_fn=attrgetter("metering_status"), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), - EnvoyCTSensorEntityDescription( - key="net_consumption_ct_status_flags", - translation_key="net_ct_status_flags", - state_class=None, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - value_fn=lambda ct: 0 if ct.status_flags is None else len(ct.status_flags), - on_phase=None, - cttype=CtType.NET_CONSUMPTION, - ), -) - - -CT_NET_CONSUMPTION_PHASE_SENSORS = { - (on_phase := PHASENAMES[phase]): [ - replace( - sensor, - key=f"{sensor.key}_l{phase + 1}", - translation_key=f"{sensor.translation_key}_phase", +# All ct types unified in common setup +CT_SENSORS = ( + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=key, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, + device_class=SensorDeviceClass.ENERGY, + suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, + suggested_display_precision=3, + value_fn=attrgetter("energy_delivered"), + on_phase=None, + cttype=cttype, + ) + for cttype, key in ( + (CtType.NET_CONSUMPTION, "lifetime_net_consumption"), + # Production CT energy_delivered is not used + (CtType.STORAGE, "lifetime_battery_discharged"), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=key, + native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, + state_class=SensorStateClass.TOTAL_INCREASING, + device_class=SensorDeviceClass.ENERGY, + suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, + suggested_display_precision=3, + value_fn=attrgetter("energy_received"), + on_phase=None, + cttype=cttype, + ) + for cttype, key in ( + (CtType.NET_CONSUMPTION, "lifetime_net_production"), + # Production CT energy_received is not used + (CtType.STORAGE, "lifetime_battery_charged"), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=key, + native_unit_of_measurement=UnitOfPower.WATT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.POWER, + suggested_unit_of_measurement=UnitOfPower.KILO_WATT, + suggested_display_precision=3, + value_fn=attrgetter("active_power"), + on_phase=None, + cttype=cttype, + ) + for cttype, key in ( + (CtType.NET_CONSUMPTION, "net_consumption"), + # Production CT active_power is not used + (CtType.STORAGE, "battery_discharge"), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=(translation_key if translation_key != "" else key), + native_unit_of_measurement=UnitOfFrequency.HERTZ, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.FREQUENCY, + suggested_display_precision=1, entity_registry_enabled_default=False, - on_phase=on_phase, - translation_placeholders={"phase_name": f"l{phase + 1}"}, + value_fn=attrgetter("frequency"), + on_phase=None, + cttype=cttype, + ) + for cttype, key, translation_key in ( + (CtType.NET_CONSUMPTION, "frequency", "net_ct_frequency"), + (CtType.PRODUCTION, "production_ct_frequency", ""), + (CtType.STORAGE, "storage_ct_frequency", ""), ) - for sensor in list(CT_NET_CONSUMPTION_SENSORS) ] - for phase in range(3) -} - -CT_PRODUCTION_SENSORS = ( - EnvoyCTSensorEntityDescription( - key="production_ct_frequency", - translation_key="production_ct_frequency", - native_unit_of_measurement=UnitOfFrequency.HERTZ, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.FREQUENCY, - suggested_display_precision=1, - entity_registry_enabled_default=False, - value_fn=attrgetter("frequency"), - on_phase=None, - cttype=CtType.PRODUCTION, - ), - EnvoyCTSensorEntityDescription( - key="production_ct_voltage", - translation_key="production_ct_voltage", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.VOLTAGE, - suggested_unit_of_measurement=UnitOfElectricPotential.VOLT, - suggested_display_precision=1, - entity_registry_enabled_default=False, - value_fn=attrgetter("voltage"), - on_phase=None, - cttype=CtType.PRODUCTION, - ), - EnvoyCTSensorEntityDescription( - key="production_ct_current", - translation_key="production_ct_current", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.CURRENT, - suggested_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - suggested_display_precision=3, - entity_registry_enabled_default=False, - value_fn=attrgetter("current"), - on_phase=None, - cttype=CtType.PRODUCTION, - ), - EnvoyCTSensorEntityDescription( - key="production_ct_powerfactor", - translation_key="production_ct_powerfactor", - device_class=SensorDeviceClass.POWER_FACTOR, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=2, - entity_registry_enabled_default=False, - value_fn=attrgetter("power_factor"), - on_phase=None, - cttype=CtType.PRODUCTION, - ), - EnvoyCTSensorEntityDescription( - key="production_ct_metering_status", - translation_key="production_ct_metering_status", - device_class=SensorDeviceClass.ENUM, - options=list(CtMeterStatus), - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - value_fn=attrgetter("metering_status"), - on_phase=None, - cttype=CtType.PRODUCTION, - ), - EnvoyCTSensorEntityDescription( - key="production_ct_status_flags", - translation_key="production_ct_status_flags", - state_class=None, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - value_fn=lambda ct: 0 if ct.status_flags is None else len(ct.status_flags), - on_phase=None, - cttype=CtType.PRODUCTION, - ), -) - -CT_PRODUCTION_PHASE_SENSORS = { - (on_phase := PHASENAMES[phase]): [ - replace( - sensor, - key=f"{sensor.key}_l{phase + 1}", - translation_key=f"{sensor.translation_key}_phase", + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=(translation_key if translation_key != "" else key), + native_unit_of_measurement=UnitOfElectricPotential.VOLT, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.VOLTAGE, + suggested_unit_of_measurement=UnitOfElectricPotential.VOLT, + suggested_display_precision=1, entity_registry_enabled_default=False, - on_phase=on_phase, - translation_placeholders={"phase_name": f"l{phase + 1}"}, + value_fn=attrgetter("voltage"), + on_phase=None, + cttype=cttype, + ) + for cttype, key, translation_key in ( + (CtType.NET_CONSUMPTION, "voltage", "net_ct_voltage"), + (CtType.PRODUCTION, "production_ct_voltage", ""), + (CtType.STORAGE, "storage_voltage", "storage_ct_voltage"), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=key, + native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.CURRENT, + suggested_unit_of_measurement=UnitOfElectricCurrent.AMPERE, + suggested_display_precision=3, + entity_registry_enabled_default=False, + value_fn=attrgetter("current"), + on_phase=None, + cttype=cttype, + ) + for cttype, key in ( + (CtType.NET_CONSUMPTION, "net_ct_current"), + (CtType.PRODUCTION, "production_ct_current"), + (CtType.STORAGE, "storage_ct_current"), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=key, + device_class=SensorDeviceClass.POWER_FACTOR, + state_class=SensorStateClass.MEASUREMENT, + suggested_display_precision=2, + entity_registry_enabled_default=False, + value_fn=attrgetter("power_factor"), + on_phase=None, + cttype=cttype, + ) + for cttype, key in ( + (CtType.NET_CONSUMPTION, "net_ct_powerfactor"), + (CtType.PRODUCTION, "production_ct_powerfactor"), + (CtType.STORAGE, "storage_ct_powerfactor"), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=(translation_key if translation_key != "" else key), + device_class=SensorDeviceClass.ENUM, + entity_category=EntityCategory.DIAGNOSTIC, + options=list(CtMeterStatus), + entity_registry_enabled_default=False, + value_fn=attrgetter("metering_status"), + on_phase=None, + cttype=cttype, + ) + for cttype, key, translation_key in ( + ( + CtType.NET_CONSUMPTION, + "net_consumption_ct_metering_status", + "net_ct_metering_status", + ), + (CtType.PRODUCTION, "production_ct_metering_status", ""), + (CtType.STORAGE, "storage_ct_metering_status", ""), + ) + ] + + [ + EnvoyCTSensorEntityDescription( + key=key, + translation_key=(translation_key if translation_key != "" else key), + state_class=None, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + value_fn=lambda ct: 0 if ct.status_flags is None else len(ct.status_flags), + on_phase=None, + cttype=cttype, + ) + for cttype, key, translation_key in ( + ( + CtType.NET_CONSUMPTION, + "net_consumption_ct_status_flags", + "net_ct_status_flags", + ), + (CtType.PRODUCTION, "production_ct_status_flags", ""), + (CtType.STORAGE, "storage_ct_status_flags", ""), ) - for sensor in list(CT_PRODUCTION_SENSORS) ] - for phase in range(3) -} - -CT_STORAGE_SENSORS = ( - EnvoyCTSensorEntityDescription( - key="lifetime_battery_discharged", - translation_key="lifetime_battery_discharged", - native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, - suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, - suggested_display_precision=3, - value_fn=attrgetter("energy_delivered"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="lifetime_battery_charged", - translation_key="lifetime_battery_charged", - native_unit_of_measurement=UnitOfEnergy.WATT_HOUR, - state_class=SensorStateClass.TOTAL_INCREASING, - device_class=SensorDeviceClass.ENERGY, - suggested_unit_of_measurement=UnitOfEnergy.MEGA_WATT_HOUR, - suggested_display_precision=3, - value_fn=attrgetter("energy_received"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="battery_discharge", - translation_key="battery_discharge", - native_unit_of_measurement=UnitOfPower.WATT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.POWER, - suggested_unit_of_measurement=UnitOfPower.KILO_WATT, - suggested_display_precision=3, - value_fn=attrgetter("active_power"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="storage_ct_frequency", - translation_key="storage_ct_frequency", - native_unit_of_measurement=UnitOfFrequency.HERTZ, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.FREQUENCY, - suggested_display_precision=1, - entity_registry_enabled_default=False, - value_fn=attrgetter("frequency"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="storage_voltage", - translation_key="storage_ct_voltage", - native_unit_of_measurement=UnitOfElectricPotential.VOLT, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.VOLTAGE, - suggested_unit_of_measurement=UnitOfElectricPotential.VOLT, - suggested_display_precision=1, - entity_registry_enabled_default=False, - value_fn=attrgetter("voltage"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="storage_ct_current", - translation_key="storage_ct_current", - native_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - state_class=SensorStateClass.MEASUREMENT, - device_class=SensorDeviceClass.CURRENT, - suggested_unit_of_measurement=UnitOfElectricCurrent.AMPERE, - suggested_display_precision=3, - entity_registry_enabled_default=False, - value_fn=attrgetter("current"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="storage_ct_powerfactor", - translation_key="storage_ct_powerfactor", - device_class=SensorDeviceClass.POWER_FACTOR, - state_class=SensorStateClass.MEASUREMENT, - suggested_display_precision=2, - entity_registry_enabled_default=False, - value_fn=attrgetter("power_factor"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="storage_ct_metering_status", - translation_key="storage_ct_metering_status", - device_class=SensorDeviceClass.ENUM, - options=list(CtMeterStatus), - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - value_fn=attrgetter("metering_status"), - on_phase=None, - cttype=CtType.STORAGE, - ), - EnvoyCTSensorEntityDescription( - key="storage_ct_status_flags", - translation_key="storage_ct_status_flags", - state_class=None, - entity_category=EntityCategory.DIAGNOSTIC, - entity_registry_enabled_default=False, - value_fn=lambda ct: 0 if ct.status_flags is None else len(ct.status_flags), - on_phase=None, - cttype=CtType.STORAGE, - ), ) -CT_STORAGE_PHASE_SENSORS = { +CT_PHASE_SENSORS = { (on_phase := PHASENAMES[phase]): [ replace( sensor, @@ -732,7 +591,7 @@ class EnvoyCTSensorEntityDescription(SensorEntityDescription): on_phase=on_phase, translation_placeholders={"phase_name": f"l{phase + 1}"}, ) - for sensor in list(CT_STORAGE_SENSORS) + for sensor in list(CT_SENSORS) ] for phase in range(3) } @@ -1060,24 +919,14 @@ async def async_setup_entry( if envoy_data.ctmeters: entities.extend( EnvoyCTEntity(coordinator, description) - for sensors in ( - CT_NET_CONSUMPTION_SENSORS, - CT_PRODUCTION_SENSORS, - CT_STORAGE_SENSORS, - ) - for description in sensors + for description in CT_SENSORS if description.cttype in envoy_data.ctmeters ) # Add Current Transformer phase entities if ctmeters_phases := envoy_data.ctmeters_phases: entities.extend( EnvoyCTPhaseEntity(coordinator, description) - for sensors in ( - CT_NET_CONSUMPTION_PHASE_SENSORS, - CT_PRODUCTION_PHASE_SENSORS, - CT_STORAGE_PHASE_SENSORS, - ) - for phase, descriptions in sensors.items() + for phase, descriptions in CT_PHASE_SENSORS.items() for description in descriptions if (cttype := description.cttype) in ctmeters_phases and phase in ctmeters_phases[cttype] diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index 353c7397d81fa..f95650853a0bb 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio +from urllib.parse import quote import voluptuous as vol @@ -152,7 +153,9 @@ def camera_image( async def stream_source(self) -> str | None: """Return the stream source.""" if self._rtsp_port: - return f"rtsp://{self._username}:{self._password}@{self._foscam_session.host}:{self._rtsp_port}/video{self._stream}" + _username = quote(self._username) + _password = quote(self._password) + return f"rtsp://{_username}:{_password}@{self._foscam_session.host}:{self._rtsp_port}/video{self._stream}" return None diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index db9caf4123949..c04fb05d055c5 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -777,7 +777,9 @@ def get(self, request: web.Request) -> web.Response: @websocket_api.websocket_command( { "type": "frontend/get_icons", - vol.Required("category"): vol.In({"entity", "entity_component", "services"}), + vol.Required("category"): vol.In( + {"entity", "entity_component", "services", "triggers", "conditions"} + ), vol.Optional("integration"): vol.All(cv.ensure_list, [str]), } ) diff --git a/homeassistant/components/goodwe/__init__.py b/homeassistant/components/goodwe/__init__.py index e191e1b775f73..d191ecb15a299 100644 --- a/homeassistant/components/goodwe/__init__.py +++ b/homeassistant/components/goodwe/__init__.py @@ -1,13 +1,14 @@ """The Goodwe inverter component.""" -from goodwe import InverterError, connect -from goodwe.const import GOODWE_TCP_PORT, GOODWE_UDP_PORT +from goodwe import Inverter, InverterError, connect +from goodwe.const import GOODWE_UDP_PORT -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.device_registry import DeviceInfo +from .config_flow import GoodweFlowHandler from .const import CONF_MODEL_FAMILY, DOMAIN, PLATFORMS from .coordinator import GoodweConfigEntry, GoodweRuntimeData, GoodweUpdateCoordinator @@ -15,28 +16,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: GoodweConfigEntry) -> bool: """Set up the Goodwe components from a config entry.""" host = entry.data[CONF_HOST] + port = entry.data.get(CONF_PORT, GOODWE_UDP_PORT) model_family = entry.data[CONF_MODEL_FAMILY] # Connect to Goodwe inverter try: inverter = await connect( host=host, - port=GOODWE_UDP_PORT, + port=port, family=model_family, retries=10, ) - except InverterError as err_udp: - # First try with UDP failed, trying with the TCP port + except InverterError as err: try: - inverter = await connect( - host=host, - port=GOODWE_TCP_PORT, - family=model_family, - retries=10, - ) + inverter = await async_check_port(hass, entry, host) except InverterError: - # Both ports are unavailable - raise ConfigEntryNotReady from err_udp + raise ConfigEntryNotReady from err device_info = DeviceInfo( configuration_url="https://www.semsportal.com", @@ -66,6 +61,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: GoodweConfigEntry) -> bo return True +async def async_check_port( + hass: HomeAssistant, entry: GoodweConfigEntry, host: str +) -> Inverter: + """Check the communication port of the inverter, it may have changed after a firmware update.""" + inverter, port = await GoodweFlowHandler.async_detect_inverter_port(host=host) + family = type(inverter).__name__ + hass.config_entries.async_update_entry( + entry, + data={ + CONF_HOST: host, + CONF_PORT: port, + CONF_MODEL_FAMILY: family, + }, + ) + return inverter + + async def async_unload_entry( hass: HomeAssistant, config_entry: GoodweConfigEntry ) -> bool: @@ -76,3 +88,31 @@ async def async_unload_entry( async def update_listener(hass: HomeAssistant, config_entry: GoodweConfigEntry) -> None: """Handle options update.""" await hass.config_entries.async_reload(config_entry.entry_id) + + +async def async_migrate_entry( + hass: HomeAssistant, config_entry: GoodweConfigEntry +) -> bool: + """Migrate old config entries.""" + + if config_entry.version > 2: + # This means the user has downgraded from a future version + return False + + if config_entry.version == 1: + # Update from version 1 to version 2 adding the PROTOCOL to the config entry + host = config_entry.data[CONF_HOST] + try: + inverter, port = await GoodweFlowHandler.async_detect_inverter_port( + host=host + ) + except InverterError as err: + raise ConfigEntryNotReady from err + new_data = { + CONF_HOST: host, + CONF_PORT: port, + CONF_MODEL_FAMILY: type(inverter).__name__, + } + hass.config_entries.async_update_entry(config_entry, data=new_data, version=2) + + return True diff --git a/homeassistant/components/goodwe/config_flow.py b/homeassistant/components/goodwe/config_flow.py index 72d27e02b2e9e..5faa2b867680c 100644 --- a/homeassistant/components/goodwe/config_flow.py +++ b/homeassistant/components/goodwe/config_flow.py @@ -5,12 +5,12 @@ import logging from typing import Any -from goodwe import InverterError, connect +from goodwe import Inverter, InverterError, connect from goodwe.const import GOODWE_TCP_PORT, GOODWE_UDP_PORT import voluptuous as vol from homeassistant.config_entries import ConfigFlow, ConfigFlowResult -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_PORT from .const import CONF_MODEL_FAMILY, DEFAULT_NAME, DOMAIN @@ -26,9 +26,15 @@ class GoodweFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a Goodwe config flow.""" - VERSION = 1 + MINOR_VERSION = 2 - async def _handle_successful_connection(self, inverter, host): + async def async_handle_successful_connection( + self, + inverter: Inverter, + host: str, + port: int, + ) -> ConfigFlowResult: + """Handle a successful connection storing it's values on the entry data.""" await self.async_set_unique_id(inverter.serial_number) self._abort_if_unique_id_configured() @@ -36,6 +42,7 @@ async def _handle_successful_connection(self, inverter, host): title=DEFAULT_NAME, data={ CONF_HOST: host, + CONF_PORT: port, CONF_MODEL_FAMILY: type(inverter).__name__, }, ) @@ -48,19 +55,26 @@ async def async_step_user( if user_input is not None: host = user_input[CONF_HOST] try: - inverter = await connect(host=host, port=GOODWE_UDP_PORT, retries=10) + inverter, port = await self.async_detect_inverter_port(host=host) except InverterError: - try: - inverter = await connect( - host=host, port=GOODWE_TCP_PORT, retries=10 - ) - except InverterError: - errors[CONF_HOST] = "connection_error" - else: - return await self._handle_successful_connection(inverter, host) + errors[CONF_HOST] = "connection_error" else: - return await self._handle_successful_connection(inverter, host) - + return await self.async_handle_successful_connection( + inverter, host, port + ) return self.async_show_form( step_id="user", data_schema=CONFIG_SCHEMA, errors=errors ) + + @staticmethod + async def async_detect_inverter_port( + host: str, + ) -> tuple[Inverter, int]: + """Detects the port of the Inverter.""" + port = GOODWE_UDP_PORT + try: + inverter = await connect(host=host, port=port, retries=10) + except InverterError: + port = GOODWE_TCP_PORT + inverter = await connect(host=host, port=port, retries=10) + return inverter, port diff --git a/homeassistant/components/lamarzocco/manifest.json b/homeassistant/components/lamarzocco/manifest.json index b8121286310a3..126878fe72ee0 100644 --- a/homeassistant/components/lamarzocco/manifest.json +++ b/homeassistant/components/lamarzocco/manifest.json @@ -37,5 +37,5 @@ "iot_class": "cloud_push", "loggers": ["pylamarzocco"], "quality_scale": "platinum", - "requirements": ["pylamarzocco==2.1.2"] + "requirements": ["pylamarzocco==2.1.3"] } diff --git a/homeassistant/components/local_todo/todo.py b/homeassistant/components/local_todo/todo.py index e6e5ca8b18b52..9eba7303d7d09 100644 --- a/homeassistant/components/local_todo/todo.py +++ b/homeassistant/components/local_todo/todo.py @@ -154,6 +154,7 @@ async def async_update(self) -> None: ), due=due, description=item.description, + completed=item.completed, ) ) self._attr_todo_items = todo_items diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index a10a926f6e50f..0a4c88124e8d2 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -58,7 +58,8 @@ Platform.SWITCH, Platform.UPDATE, ] -DEVICE_UPDATE_INTERVAL = timedelta(seconds=60) +DEVICE_UPDATE_INTERVAL_MIN = timedelta(seconds=60) +DEVICE_UPDATE_INTERVAL_PER_CAM = timedelta(seconds=10) FIRMWARE_UPDATE_INTERVAL = timedelta(hours=24) NUM_CRED_ERRORS = 3 @@ -137,9 +138,12 @@ async def async_setup_entry( } hass.config_entries.async_update_entry(config_entry, data=data) + min_timeout = host.api.timeout * (RETRY_ATTEMPTS + 2) + update_timeout = max(min_timeout, min_timeout * host.api.num_cameras / 10) + async def async_device_config_update() -> None: """Update the host state cache and renew the ONVIF-subscription.""" - async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)): + async with asyncio.timeout(update_timeout): try: await host.update_states() except CredentialsInvalidError as err: @@ -156,7 +160,7 @@ async def async_device_config_update() -> None: host.credential_errors = 0 - async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)): + async with asyncio.timeout(min_timeout): await host.renew() if host.api.new_devices and config_entry.state == ConfigEntryState.LOADED: @@ -171,7 +175,7 @@ async def async_device_config_update() -> None: async def async_check_firmware_update() -> None: """Check for firmware updates.""" - async with asyncio.timeout(host.api.timeout * (RETRY_ATTEMPTS + 2)): + async with asyncio.timeout(min_timeout): try: await host.api.check_new_firmware(host.firmware_ch_list) except ReolinkError as err: @@ -197,7 +201,10 @@ async def async_check_firmware_update() -> None: config_entry=config_entry, name=f"reolink.{host.api.nvr_name}", update_method=async_device_config_update, - update_interval=DEVICE_UPDATE_INTERVAL, + update_interval=max( + DEVICE_UPDATE_INTERVAL_MIN, + DEVICE_UPDATE_INTERVAL_PER_CAM * host.api.num_cameras, + ), ) firmware_coordinator = DataUpdateCoordinator( hass, diff --git a/homeassistant/components/reolink/update.py b/homeassistant/components/reolink/update.py index a7c883003b731..7b5bb9077d2b7 100644 --- a/homeassistant/components/reolink/update.py +++ b/homeassistant/components/reolink/update.py @@ -23,7 +23,7 @@ DataUpdateCoordinator, ) -from . import DEVICE_UPDATE_INTERVAL +from . import DEVICE_UPDATE_INTERVAL_MIN, DEVICE_UPDATE_INTERVAL_PER_CAM from .const import DOMAIN from .entity import ( ReolinkChannelCoordinatorEntity, @@ -221,7 +221,10 @@ async def _pause_update_coordinator(self) -> None: async def _resume_update_coordinator(self, *args: Any) -> None: """Resume updating the states using the data update coordinator (after reboots).""" - self._reolink_data.device_coordinator.update_interval = DEVICE_UPDATE_INTERVAL + self._reolink_data.device_coordinator.update_interval = max( + DEVICE_UPDATE_INTERVAL_MIN, + DEVICE_UPDATE_INTERVAL_PER_CAM * self._host.api.num_cameras, + ) try: await self._reolink_data.device_coordinator.async_refresh() finally: diff --git a/homeassistant/components/ruuvitag_ble/__init__.py b/homeassistant/components/ruuvitag_ble/__init__.py index 86a0b2cd40a15..bf741a99a138c 100644 --- a/homeassistant/components/ruuvitag_ble/__init__.py +++ b/homeassistant/components/ruuvitag_ble/__init__.py @@ -22,7 +22,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Set up Ruuvitag BLE device from a config entry.""" + """Set up Ruuvi BLE device from a config entry.""" address = entry.unique_id assert address is not None data = RuuvitagBluetoothDeviceData() diff --git a/homeassistant/components/ruuvitag_ble/manifest.json b/homeassistant/components/ruuvitag_ble/manifest.json index 7b35f42597d1c..b14c14b87eafb 100644 --- a/homeassistant/components/ruuvitag_ble/manifest.json +++ b/homeassistant/components/ruuvitag_ble/manifest.json @@ -1,6 +1,6 @@ { "domain": "ruuvitag_ble", - "name": "RuuviTag BLE", + "name": "Ruuvi BLE", "bluetooth": [ { "connectable": false, diff --git a/homeassistant/components/ruuvitag_ble/sensor.py b/homeassistant/components/ruuvitag_ble/sensor.py index 4c49306e96654..7873878818aee 100644 --- a/homeassistant/components/ruuvitag_ble/sensor.py +++ b/homeassistant/components/ruuvitag_ble/sensor.py @@ -191,7 +191,7 @@ async def async_setup_entry( entry: config_entries.ConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: - """Set up the Ruuvitag BLE sensors.""" + """Set up the Ruuvi BLE sensors.""" coordinator: PassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][ entry.entry_id ] @@ -210,7 +210,7 @@ class RuuvitagBluetoothSensorEntity( ], SensorEntity, ): - """Representation of a Ruuvitag BLE sensor.""" + """Representation of a Ruuvi BLE sensor.""" @property def native_value(self) -> int | float | None: diff --git a/homeassistant/components/satel_integra/alarm_control_panel.py b/homeassistant/components/satel_integra/alarm_control_panel.py index ffb46d4dbe8ef..d17c7d995b4d2 100644 --- a/homeassistant/components/satel_integra/alarm_control_panel.py +++ b/homeassistant/components/satel_integra/alarm_control_panel.py @@ -13,20 +13,19 @@ AlarmControlPanelState, CodeFormat, ) -from homeassistant.const import CONF_NAME +from homeassistant.config_entries import ConfigSubentry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( CONF_ARM_HOME_MODE, CONF_PARTITION_NUMBER, - DOMAIN, SIGNAL_PANEL_MESSAGE, SUBENTRY_TYPE_PARTITION, SatelConfigEntry, ) +from .entity import SatelIntegraEntity ALARM_STATE_MAP = { AlarmState.TRIGGERED: AlarmControlPanelState.TRIGGERED, @@ -59,54 +58,49 @@ async def async_setup_entry( for subentry in partition_subentries: partition_num: int = subentry.data[CONF_PARTITION_NUMBER] - zone_name: str = subentry.data[CONF_NAME] arm_home_mode: int = subentry.data[CONF_ARM_HOME_MODE] async_add_entities( [ SatelIntegraAlarmPanel( controller, - zone_name, - arm_home_mode, - partition_num, config_entry.entry_id, + subentry, + partition_num, + arm_home_mode, ) ], config_subentry_id=subentry.subentry_id, ) -class SatelIntegraAlarmPanel(AlarmControlPanelEntity): +class SatelIntegraAlarmPanel(SatelIntegraEntity, AlarmControlPanelEntity): """Representation of an AlarmDecoder-based alarm panel.""" _attr_code_format = CodeFormat.NUMBER - _attr_should_poll = False _attr_supported_features = ( AlarmControlPanelEntityFeature.ARM_HOME | AlarmControlPanelEntityFeature.ARM_AWAY ) - _attr_has_entity_name = True - _attr_name = None - def __init__( self, controller: AsyncSatel, - device_name: str, - arm_home_mode: int, - partition_id: int, config_entry_id: str, + subentry: ConfigSubentry, + device_number: int, + arm_home_mode: int, ) -> None: """Initialize the alarm panel.""" - self._attr_unique_id = f"{config_entry_id}_alarm_panel_{partition_id}" - self._arm_home_mode = arm_home_mode - self._partition_id = partition_id - self._satel = controller - - self._attr_device_info = DeviceInfo( - name=device_name, identifiers={(DOMAIN, self._attr_unique_id)} + super().__init__( + controller, + config_entry_id, + subentry, + device_number, ) + self._arm_home_mode = arm_home_mode + async def async_added_to_hass(self) -> None: """Update alarm status and register callbacks for future updates.""" self._attr_alarm_state = self._read_alarm_state() @@ -136,7 +130,7 @@ def _read_alarm_state(self) -> AlarmControlPanelState | None: for satel_state, ha_state in ALARM_STATE_MAP.items(): if ( satel_state in self._satel.partition_states - and self._partition_id in self._satel.partition_states[satel_state] + and self._device_number in self._satel.partition_states[satel_state] ): return ha_state @@ -152,21 +146,21 @@ async def async_alarm_disarm(self, code: str | None = None) -> None: self._attr_alarm_state == AlarmControlPanelState.TRIGGERED ) - await self._satel.disarm(code, [self._partition_id]) + await self._satel.disarm(code, [self._device_number]) if clear_alarm_necessary: # Wait 1s before clearing the alarm await asyncio.sleep(1) - await self._satel.clear_alarm(code, [self._partition_id]) + await self._satel.clear_alarm(code, [self._device_number]) async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" if code: - await self._satel.arm(code, [self._partition_id]) + await self._satel.arm(code, [self._device_number]) async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" if code: - await self._satel.arm(code, [self._partition_id], self._arm_home_mode) + await self._satel.arm(code, [self._device_number], self._arm_home_mode) diff --git a/homeassistant/components/satel_integra/binary_sensor.py b/homeassistant/components/satel_integra/binary_sensor.py index 9a5b99cc49bd2..94e791532cf98 100644 --- a/homeassistant/components/satel_integra/binary_sensor.py +++ b/homeassistant/components/satel_integra/binary_sensor.py @@ -8,25 +8,22 @@ BinarySensorDeviceClass, BinarySensorEntity, ) -from homeassistant.const import CONF_NAME +from homeassistant.config_entries import ConfigSubentry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( CONF_OUTPUT_NUMBER, - CONF_OUTPUTS, CONF_ZONE_NUMBER, CONF_ZONE_TYPE, - CONF_ZONES, - DOMAIN, SIGNAL_OUTPUTS_UPDATED, SIGNAL_ZONES_UPDATED, SUBENTRY_TYPE_OUTPUT, SUBENTRY_TYPE_ZONE, SatelConfigEntry, ) +from .entity import SatelIntegraEntity async def async_setup_entry( @@ -46,18 +43,16 @@ async def async_setup_entry( for subentry in zone_subentries: zone_num: int = subentry.data[CONF_ZONE_NUMBER] zone_type: BinarySensorDeviceClass = subentry.data[CONF_ZONE_TYPE] - zone_name: str = subentry.data[CONF_NAME] async_add_entities( [ SatelIntegraBinarySensor( controller, + config_entry.entry_id, + subentry, zone_num, - zone_name, zone_type, - CONF_ZONES, SIGNAL_ZONES_UPDATED, - config_entry.entry_id, ) ], config_subentry_id=subentry.subentry_id, @@ -71,51 +66,44 @@ async def async_setup_entry( for subentry in output_subentries: output_num: int = subentry.data[CONF_OUTPUT_NUMBER] ouput_type: BinarySensorDeviceClass = subentry.data[CONF_ZONE_TYPE] - output_name: str = subentry.data[CONF_NAME] async_add_entities( [ SatelIntegraBinarySensor( controller, + config_entry.entry_id, + subentry, output_num, - output_name, ouput_type, - CONF_OUTPUTS, SIGNAL_OUTPUTS_UPDATED, - config_entry.entry_id, ) ], config_subentry_id=subentry.subentry_id, ) -class SatelIntegraBinarySensor(BinarySensorEntity): +class SatelIntegraBinarySensor(SatelIntegraEntity, BinarySensorEntity): """Representation of an Satel Integra binary sensor.""" - _attr_should_poll = False - _attr_has_entity_name = True - _attr_name = None - def __init__( self, controller: AsyncSatel, + config_entry_id: str, + subentry: ConfigSubentry, device_number: int, - device_name: str, device_class: BinarySensorDeviceClass, - sensor_type: str, react_to_signal: str, - config_entry_id: str, ) -> None: """Initialize the binary_sensor.""" - self._device_number = device_number - self._attr_unique_id = f"{config_entry_id}_{sensor_type}_{device_number}" - self._react_to_signal = react_to_signal - self._satel = controller + super().__init__( + controller, + config_entry_id, + subentry, + device_number, + ) self._attr_device_class = device_class - self._attr_device_info = DeviceInfo( - name=device_name, identifiers={(DOMAIN, self._attr_unique_id)} - ) + self._react_to_signal = react_to_signal async def async_added_to_hass(self) -> None: """Register callbacks.""" diff --git a/homeassistant/components/satel_integra/entity.py b/homeassistant/components/satel_integra/entity.py new file mode 100644 index 0000000000000..0d333c829463b --- /dev/null +++ b/homeassistant/components/satel_integra/entity.py @@ -0,0 +1,58 @@ +"""Satel Integra base entity.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from satel_integra.satel_integra import AsyncSatel + +from homeassistant.config_entries import ConfigSubentry +from homeassistant.const import CONF_NAME +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.entity import Entity + +from .const import ( + DOMAIN, + SUBENTRY_TYPE_OUTPUT, + SUBENTRY_TYPE_PARTITION, + SUBENTRY_TYPE_SWITCHABLE_OUTPUT, + SUBENTRY_TYPE_ZONE, +) + +SubentryTypeToEntityType: dict[str, str] = { + SUBENTRY_TYPE_PARTITION: "alarm_panel", + SUBENTRY_TYPE_SWITCHABLE_OUTPUT: "switch", + SUBENTRY_TYPE_ZONE: "zones", + SUBENTRY_TYPE_OUTPUT: "outputs", +} + + +class SatelIntegraEntity(Entity): + """Defines a base Satel Integra entity.""" + + _attr_should_poll = False + _attr_has_entity_name = True + _attr_name = None + + def __init__( + self, + controller: AsyncSatel, + config_entry_id: str, + subentry: ConfigSubentry, + device_number: int, + ) -> None: + """Initialize the Satel Integra entity.""" + + self._satel = controller + self._device_number = device_number + + entity_type = SubentryTypeToEntityType[subentry.subentry_type] + + if TYPE_CHECKING: + assert entity_type is not None + + self._attr_unique_id = f"{config_entry_id}_{entity_type}_{device_number}" + + self._attr_device_info = DeviceInfo( + name=subentry.data[CONF_NAME], identifiers={(DOMAIN, self._attr_unique_id)} + ) diff --git a/homeassistant/components/satel_integra/switch.py b/homeassistant/components/satel_integra/switch.py index a9a72c74db609..4ae84e3312e26 100644 --- a/homeassistant/components/satel_integra/switch.py +++ b/homeassistant/components/satel_integra/switch.py @@ -7,19 +7,19 @@ from satel_integra.satel_integra import AsyncSatel from homeassistant.components.switch import SwitchEntity -from homeassistant.const import CONF_CODE, CONF_NAME +from homeassistant.config_entries import ConfigSubentry +from homeassistant.const import CONF_CODE from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( CONF_SWITCHABLE_OUTPUT_NUMBER, - DOMAIN, SIGNAL_OUTPUTS_UPDATED, SUBENTRY_TYPE_SWITCHABLE_OUTPUT, SatelConfigEntry, ) +from .entity import SatelIntegraEntity async def async_setup_entry( @@ -38,47 +38,42 @@ async def async_setup_entry( for subentry in switchable_output_subentries: switchable_output_num: int = subentry.data[CONF_SWITCHABLE_OUTPUT_NUMBER] - switchable_output_name: str = subentry.data[CONF_NAME] async_add_entities( [ SatelIntegraSwitch( controller, + config_entry.entry_id, + subentry, switchable_output_num, - switchable_output_name, config_entry.options.get(CONF_CODE), - config_entry.entry_id, ), ], config_subentry_id=subentry.subentry_id, ) -class SatelIntegraSwitch(SwitchEntity): - """Representation of an Satel switch.""" - - _attr_should_poll = False - _attr_has_entity_name = True - _attr_name = None +class SatelIntegraSwitch(SatelIntegraEntity, SwitchEntity): + """Representation of an Satel Integra switch.""" def __init__( self, controller: AsyncSatel, + config_entry_id: str, + subentry: ConfigSubentry, device_number: int, - device_name: str, code: str | None, - config_entry_id: str, ) -> None: """Initialize the switch.""" - self._device_number = device_number - self._attr_unique_id = f"{config_entry_id}_switch_{device_number}" - self._code = code - self._satel = controller - - self._attr_device_info = DeviceInfo( - name=device_name, identifiers={(DOMAIN, self._attr_unique_id)} + super().__init__( + controller, + config_entry_id, + subentry, + device_number, ) + self._code = code + async def async_added_to_hass(self) -> None: """Register callbacks.""" self._attr_is_on = self._device_number in self._satel.violated_outputs diff --git a/homeassistant/components/senz/__init__.py b/homeassistant/components/senz/__init__.py index 83f619d21da85..59a5f3cf2762c 100644 --- a/homeassistant/components/senz/__init__.py +++ b/homeassistant/components/senz/__init__.py @@ -3,15 +3,17 @@ from __future__ import annotations from datetime import timedelta +from http import HTTPStatus import logging from aiosenz import SENZAPI, Thermostat -from httpx import RequestError +from httpx import HTTPStatusError, RequestError +import jwt from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv, httpx_client from homeassistant.helpers.config_entry_oauth2_flow import ( ImplementationUnavailableError, @@ -58,8 +60,21 @@ async def update_thermostats() -> dict[str, Thermostat]: try: account = await senz_api.get_account() + except HTTPStatusError as err: + if err.response.status_code == HTTPStatus.UNAUTHORIZED: + raise ConfigEntryAuthFailed( + translation_domain=DOMAIN, + translation_key="config_entry_auth_failed", + ) from err + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="config_entry_not_ready", + ) from err except RequestError as err: - raise ConfigEntryNotReady from err + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="config_entry_not_ready", + ) from err coordinator: SENZDataUpdateCoordinator = DataUpdateCoordinator( hass, @@ -82,3 +97,27 @@ async def update_thermostats() -> dict[str, Thermostat]: async def async_unload_entry(hass: HomeAssistant, entry: SENZConfigEntry) -> bool: """Unload a config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + + +async def async_migrate_entry( + hass: HomeAssistant, config_entry: SENZConfigEntry +) -> bool: + """Migrate old entry.""" + + # Use sub(ject) from access_token as unique_id + if config_entry.version == 1 and config_entry.minor_version == 1: + token = jwt.decode( + config_entry.data["token"]["access_token"], + options={"verify_signature": False}, + ) + uid = token["sub"] + hass.config_entries.async_update_entry( + config_entry, unique_id=uid, minor_version=2 + ) + _LOGGER.info( + "Migration to version %s.%s successful", + config_entry.version, + config_entry.minor_version, + ) + + return True diff --git a/homeassistant/components/senz/config_flow.py b/homeassistant/components/senz/config_flow.py index 457c4f10dd8da..8dc164f229c19 100644 --- a/homeassistant/components/senz/config_flow.py +++ b/homeassistant/components/senz/config_flow.py @@ -1,7 +1,16 @@ """Config flow for nVent RAYCHEM SENZ.""" +from collections.abc import Mapping import logging +from typing import Any +import jwt + +from homeassistant.config_entries import ( + SOURCE_REAUTH, + SOURCE_RECONFIGURE, + ConfigFlowResult, +) from homeassistant.helpers import config_entry_oauth2_flow from .const import DOMAIN @@ -12,6 +21,8 @@ class OAuth2FlowHandler( ): """Config flow to handle SENZ OAuth2 authentication.""" + VERSION = 1 + MINOR_VERSION = 2 DOMAIN = DOMAIN @property @@ -23,3 +34,49 @@ def logger(self) -> logging.Logger: def extra_authorize_data(self) -> dict: """Extra data that needs to be appended to the authorize url.""" return {"scope": "restapi offline_access"} + + async def async_step_reauth( + self, entry_data: Mapping[str, Any] + ) -> ConfigFlowResult: + """Perform reauth upon an API authentication error.""" + + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Dialog that informs the user that reauth is required.""" + if user_input is None: + return self.async_show_form(step_id="reauth_confirm") + + return await self.async_step_user() + + async def async_step_reconfigure( + self, user_input: Mapping[str, Any] | None = None + ) -> ConfigFlowResult: + """User initiated reconfiguration.""" + return await self.async_step_user() + + async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult: + """Create or update the config entry.""" + + token = jwt.decode( + data["token"]["access_token"], options={"verify_signature": False} + ) + uid = token["sub"] + await self.async_set_unique_id(uid) + + if self.source == SOURCE_REAUTH: + self._abort_if_unique_id_mismatch(reason="account_mismatch") + return self.async_update_reload_and_abort( + self._get_reauth_entry(), data=data + ) + + if self.source == SOURCE_RECONFIGURE: + self._abort_if_unique_id_mismatch(reason="account_mismatch") + return self.async_update_reload_and_abort( + self._get_reconfigure_entry(), data=data + ) + + self._abort_if_unique_id_configured() + return await super().async_oauth_create_entry(data) diff --git a/homeassistant/components/senz/strings.json b/homeassistant/components/senz/strings.json index 7f64583385474..cc259261ba315 100644 --- a/homeassistant/components/senz/strings.json +++ b/homeassistant/components/senz/strings.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "account_mismatch": "The used account does not match the original account", "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", @@ -9,7 +10,9 @@ "oauth_error": "[%key:common::config_flow::abort::oauth2_error%]", "oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]", "oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]", - "oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]" + "oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" }, "create_entry": { "default": "[%key:common::config_flow::create_entry::authenticated%]" @@ -23,10 +26,20 @@ "implementation": "[%key:common::config_flow::description::implementation%]" }, "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" + }, + "reauth_confirm": { + "description": "The SENZ integration needs to re-authenticate your account", + "title": "[%key:common::config_flow::title::reauth%]" } } }, "exceptions": { + "config_entry_auth_failed": { + "message": "Authentication failed. Please log in again." + }, + "config_entry_not_ready": { + "message": "Error while loading the integration." + }, "oauth2_implementation_unavailable": { "message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]" } diff --git a/homeassistant/components/smartthings/manifest.json b/homeassistant/components/smartthings/manifest.json index 14d61885e5fa6..a225a28550dcd 100644 --- a/homeassistant/components/smartthings/manifest.json +++ b/homeassistant/components/smartthings/manifest.json @@ -30,5 +30,5 @@ "iot_class": "cloud_push", "loggers": ["pysmartthings"], "quality_scale": "bronze", - "requirements": ["pysmartthings==3.3.2"] + "requirements": ["pysmartthings==3.3.3"] } diff --git a/homeassistant/components/telegram_bot/manifest.json b/homeassistant/components/telegram_bot/manifest.json index d73e55cafe4f9..0d320cfe3b088 100644 --- a/homeassistant/components/telegram_bot/manifest.json +++ b/homeassistant/components/telegram_bot/manifest.json @@ -7,6 +7,6 @@ "documentation": "https://www.home-assistant.io/integrations/telegram_bot", "iot_class": "cloud_push", "loggers": ["telegram"], - "quality_scale": "bronze", + "quality_scale": "silver", "requirements": ["python-telegram-bot[socks]==22.1"] } diff --git a/homeassistant/components/telegram_bot/quality_scale.yaml b/homeassistant/components/telegram_bot/quality_scale.yaml index 8b7ff4eaa7276..d5aeba8384f27 100644 --- a/homeassistant/components/telegram_bot/quality_scale.yaml +++ b/homeassistant/components/telegram_bot/quality_scale.yaml @@ -26,7 +26,7 @@ rules: unique-config-entry: done # Silver - action-exceptions: todo + action-exceptions: done config-entry-unloading: done docs-configuration-parameters: done docs-installation-parameters: done diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index a37dd18120c4f..ae734f4fc6713 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -219,7 +219,6 @@ def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-ini self._attr_code_arm_required: bool = config[CONF_CODE_ARM_REQUIRED] self._attr_code_format = config[CONF_CODE_FORMAT].value - self._state: AlarmControlPanelState | None = None self._attr_supported_features: AlarmControlPanelEntityFeature = ( AlarmControlPanelEntityFeature(0) ) @@ -244,11 +243,6 @@ def _iterate_scripts( if (action_config := config.get(action_id)) is not None: yield (action_id, action_config, supported_feature) - @property - def alarm_state(self) -> AlarmControlPanelState | None: - """Return the state of the device.""" - return self._state - async def _async_handle_restored_state(self) -> None: if ( (last_state := await self.async_get_last_state()) is not None @@ -256,14 +250,14 @@ async def _async_handle_restored_state(self) -> None: and last_state.state in _VALID_STATES # The trigger might have fired already while we waited for stored data, # then we should not restore state - and self._state is None + and self._attr_alarm_state is None ): - self._state = AlarmControlPanelState(last_state.state) + self._attr_alarm_state = AlarmControlPanelState(last_state.state) def _handle_state(self, result: Any) -> None: # Validate state if result in _VALID_STATES: - self._state = result + self._attr_alarm_state = result _LOGGER.debug("Valid state - %s", result) return @@ -273,7 +267,7 @@ def _handle_state(self, result: Any) -> None: self.entity_id, ", ".join(_VALID_STATES), ) - self._state = None + self._attr_alarm_state = None async def _async_alarm_arm(self, state: Any, script: Script | None, code: Any): """Arm the panel to specified state with supplied script.""" @@ -284,7 +278,7 @@ async def _async_alarm_arm(self, state: Any, script: Script | None, code: Any): ) if self._attr_assumed_state: - self._state = state + self._attr_alarm_state = state self.async_write_ha_state() async def async_alarm_arm_away(self, code: str | None = None) -> None: @@ -376,7 +370,7 @@ async def async_added_to_hass(self) -> None: @callback def _update_state(self, result): if isinstance(result, TemplateError): - self._state = None + self._attr_alarm_state = None return self._handle_state(result) @@ -386,7 +380,7 @@ def _async_setup_templates(self) -> None: """Set up templates.""" if self._template: self.add_template_attribute( - "_state", self._template, None, self._update_state + "_attr_alarm_state", self._template, None, self._update_state ) super()._async_setup_templates() diff --git a/homeassistant/components/todo/__init__.py b/homeassistant/components/todo/__init__.py index ea0448b74997d..86004f931cccb 100644 --- a/homeassistant/components/todo/__init__.py +++ b/homeassistant/components/todo/__init__.py @@ -227,6 +227,9 @@ class TodoItem: description: str | None = None """A more complete description than that provided by the summary.""" + completed: datetime.datetime | None = None + """The date and time that a to-do item was marked completed.""" + CACHED_PROPERTIES_WITH_ATTR_ = { "todo_items", diff --git a/homeassistant/components/tuya/alarm_control_panel.py b/homeassistant/components/tuya/alarm_control_panel.py index 90e3da660f91f..24cb951d59748 100644 --- a/homeassistant/components/tuya/alarm_control_panel.py +++ b/homeassistant/components/tuya/alarm_control_panel.py @@ -3,8 +3,7 @@ from __future__ import annotations from base64 import b64decode -from dataclasses import dataclass -from enum import StrEnum +from typing import Any from tuya_sharing import CustomerDevice, Manager @@ -21,52 +20,87 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode from .entity import TuyaEntity -from .models import DPCodeEnumWrapper -from .util import get_dpcode +from .models import DPCodeBase64Wrapper, DPCodeEnumWrapper - -@dataclass(frozen=True) -class TuyaAlarmControlPanelEntityDescription(AlarmControlPanelEntityDescription): - """Describe a Tuya Alarm Control Panel entity.""" - - master_state: DPCode | None = None - alarm_msg: DPCode | None = None - - -class Mode(StrEnum): - """Alarm modes.""" - - ARM = "arm" - DISARMED = "disarmed" - HOME = "home" - SOS = "sos" +ALARM: dict[DeviceCategory, tuple[AlarmControlPanelEntityDescription, ...]] = { + DeviceCategory.MAL: ( + AlarmControlPanelEntityDescription( + key=DPCode.MASTER_MODE, + name="Alarm", + ), + ) +} -class State(StrEnum): - """Alarm states.""" +class _AlarmChangedByWrapper(DPCodeBase64Wrapper): + """Wrapper for changed_by. - NORMAL = "normal" - ALARM = "alarm" + Decode base64 to utf-16be string, but only if alarm has been triggered. + """ + def read_device_status(self, device: CustomerDevice) -> str | None: + """Read the device status.""" + if ( + device.status.get(DPCode.MASTER_STATE) != "alarm" + or (data := self.read_bytes(device)) is None + ): + return None + return data.decode("utf-16be") + + +class _AlarmModeWrapper(DPCodeEnumWrapper): + """Wrapper for the alarm mode of a device. + + Handles alarm mode enum values and determines the alarm state, + including logic for detecting when the alarm is triggered and + distinguishing triggered state from battery warnings. + """ + + _ACTION_MAPPINGS = { + # Home Assistant action => Tuya device mode + "arm_home": "home", + "arm_away": "arm", + "disarm": "disarmed", + "trigger": "sos", + } + _STATE_MAPPINGS = { + # Tuya device mode => Home Assistant panel state + "disarmed": AlarmControlPanelState.DISARMED, + "arm": AlarmControlPanelState.ARMED_AWAY, + "home": AlarmControlPanelState.ARMED_HOME, + "sos": AlarmControlPanelState.TRIGGERED, + } + + def read_panel_state(self, device: CustomerDevice) -> AlarmControlPanelState | None: + """Read the device status.""" + # When the alarm is triggered, only its 'state' is changing. From 'normal' to 'alarm'. + # The 'mode' doesn't change, and stays as 'arm' or 'home'. + if device.status.get(DPCode.MASTER_STATE) == "alarm": + # Only report as triggered if NOT a battery warning + if not ( + (encoded_msg := device.status.get(DPCode.ALARM_MSG)) + and (decoded_message := b64decode(encoded_msg).decode("utf-16be")) + and "Sensor Low Battery" in decoded_message + ): + return AlarmControlPanelState.TRIGGERED -STATE_MAPPING: dict[str, AlarmControlPanelState] = { - Mode.DISARMED: AlarmControlPanelState.DISARMED, - Mode.ARM: AlarmControlPanelState.ARMED_AWAY, - Mode.HOME: AlarmControlPanelState.ARMED_HOME, - Mode.SOS: AlarmControlPanelState.TRIGGERED, -} + if (status := self.read_device_status(device)) is None: + return None + return self._STATE_MAPPINGS.get(status) + def supports_action(self, action: str) -> bool: + """Return if action is supported.""" + return ( + mapped_value := self._ACTION_MAPPINGS.get(action) + ) is not None and mapped_value in self.type_information.range -ALARM: dict[DeviceCategory, tuple[TuyaAlarmControlPanelEntityDescription, ...]] = { - DeviceCategory.MAL: ( - TuyaAlarmControlPanelEntityDescription( - key=DPCode.MASTER_MODE, - master_state=DPCode.MASTER_STATE, - alarm_msg=DPCode.ALARM_MSG, - name="Alarm", - ), - ) -} + def _convert_value_to_raw_value(self, device: CustomerDevice, value: Any) -> Any: + """Convert value to raw value.""" + if ( + mapped_value := self._ACTION_MAPPINGS.get(value) + ) is not None and mapped_value in self.type_information.range: + return mapped_value + raise ValueError(f"Unsupported value {value} for {self.dpcode}") async def async_setup_entry( @@ -89,15 +123,15 @@ def async_discover_device(device_ids: list[str]) -> None: device, manager, description, - action_dpcode_wrapper=action_dpcode_wrapper, - state_dpcode_wrapper=DPCodeEnumWrapper.find_dpcode( - device, description.master_state + mode_wrapper=mode_wrapper, + changed_by_wrapper=_AlarmChangedByWrapper.find_dpcode( + device, DPCode.ALARM_MSG ), ) for description in descriptions if ( - action_dpcode_wrapper := DPCodeEnumWrapper.find_dpcode( - device, description.key, prefer_function=True + mode_wrapper := _AlarmModeWrapper.find_dpcode( + device, DPCode.MASTER_MODE, prefer_function=True ) ) ) @@ -115,79 +149,55 @@ class TuyaAlarmEntity(TuyaEntity, AlarmControlPanelEntity): _attr_name = None _attr_code_arm_required = False - _alarm_msg_dpcode: DPCode | None = None def __init__( self, device: CustomerDevice, device_manager: Manager, - description: TuyaAlarmControlPanelEntityDescription, + description: AlarmControlPanelEntityDescription, *, - action_dpcode_wrapper: DPCodeEnumWrapper, - state_dpcode_wrapper: DPCodeEnumWrapper | None, + mode_wrapper: _AlarmModeWrapper, + changed_by_wrapper: _AlarmChangedByWrapper | None, ) -> None: """Init Tuya Alarm.""" super().__init__(device, device_manager) self.entity_description = description self._attr_unique_id = f"{super().unique_id}{description.key}" - self._action_dpcode_wrapper = action_dpcode_wrapper - self._state_dpcode_wrapper = state_dpcode_wrapper + self._mode_wrapper = mode_wrapper + self._changed_by_wrapper = changed_by_wrapper - # Determine supported modes - if Mode.HOME in action_dpcode_wrapper.type_information.range: + # Determine supported modes + if mode_wrapper.supports_action("arm_home"): self._attr_supported_features |= AlarmControlPanelEntityFeature.ARM_HOME - if Mode.ARM in action_dpcode_wrapper.type_information.range: + if mode_wrapper.supports_action("arm_away"): self._attr_supported_features |= AlarmControlPanelEntityFeature.ARM_AWAY - if Mode.SOS in action_dpcode_wrapper.type_information.range: + if mode_wrapper.supports_action("trigger"): self._attr_supported_features |= AlarmControlPanelEntityFeature.TRIGGER - # Determine alarm message - if dp_code := get_dpcode(self.device, description.alarm_msg): - self._alarm_msg_dpcode = dp_code - @property def alarm_state(self) -> AlarmControlPanelState | None: """Return the state of the device.""" - # When the alarm is triggered, only its 'state' is changing. From 'normal' to 'alarm'. - # The 'mode' doesn't change, and stays as 'arm' or 'home'. - if ( - self._state_dpcode_wrapper is not None - and self.device.status.get(self._state_dpcode_wrapper.dpcode) == State.ALARM - ): - # Only report as triggered if NOT a battery warning - if ( - changed_by := self.changed_by - ) is None or "Sensor Low Battery" not in changed_by: - return AlarmControlPanelState.TRIGGERED - - if not (status := self.device.status.get(self.entity_description.key)): - return None - return STATE_MAPPING.get(status) + return self._mode_wrapper.read_panel_state(self.device) @property def changed_by(self) -> str | None: """Last change triggered by.""" - if ( - self._state_dpcode_wrapper is not None - and self._alarm_msg_dpcode is not None - and self.device.status.get(self._state_dpcode_wrapper.dpcode) == State.ALARM - and (encoded_msg := self.device.status.get(self._alarm_msg_dpcode)) - ): - return b64decode(encoded_msg).decode("utf-16be") - return None + if self._changed_by_wrapper is None: + return None + return self._changed_by_wrapper.read_device_status(self.device) async def async_alarm_disarm(self, code: str | None = None) -> None: """Send Disarm command.""" - await self._async_send_dpcode_update(self._action_dpcode_wrapper, Mode.DISARMED) + await self._async_send_dpcode_update(self._mode_wrapper, "disarm") async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send Home command.""" - await self._async_send_dpcode_update(self._action_dpcode_wrapper, Mode.HOME) + await self._async_send_dpcode_update(self._mode_wrapper, "arm_home") async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send Arm command.""" - await self._async_send_dpcode_update(self._action_dpcode_wrapper, Mode.ARM) + await self._async_send_dpcode_update(self._mode_wrapper, "arm_away") async def async_alarm_trigger(self, code: str | None = None) -> None: """Send SOS command.""" - await self._async_send_dpcode_update(self._action_dpcode_wrapper, Mode.SOS) + await self._async_send_dpcode_update(self._mode_wrapper, "trigger") diff --git a/homeassistant/components/tuya/camera.py b/homeassistant/components/tuya/camera.py index 93525c723da2e..7dbe355b107a0 100644 --- a/homeassistant/components/tuya/camera.py +++ b/homeassistant/components/tuya/camera.py @@ -13,6 +13,7 @@ from . import TuyaConfigEntry from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode from .entity import TuyaEntity +from .models import DPCodeBooleanWrapper CAMERAS: tuple[DeviceCategory, ...] = ( DeviceCategory.DGHSXJ, @@ -35,7 +36,18 @@ def async_discover_device(device_ids: list[str]) -> None: for device_id in device_ids: device = manager.device_map[device_id] if device.category in CAMERAS: - entities.append(TuyaCameraEntity(device, manager)) + entities.append( + TuyaCameraEntity( + device, + manager, + motion_detection_switch=DPCodeBooleanWrapper.find_dpcode( + device, DPCode.MOTION_SWITCH, prefer_function=True + ), + recording_status=DPCodeBooleanWrapper.find_dpcode( + device, DPCode.RECORD_SWITCH + ), + ) + ) async_add_entities(entities) @@ -57,21 +69,30 @@ def __init__( self, device: CustomerDevice, device_manager: Manager, + *, + motion_detection_switch: DPCodeBooleanWrapper | None = None, + recording_status: DPCodeBooleanWrapper | None = None, ) -> None: """Init Tuya Camera.""" super().__init__(device, device_manager) CameraEntity.__init__(self) self._attr_model = device.product_name + self._motion_detection_switch = motion_detection_switch + self._recording_status = recording_status @property def is_recording(self) -> bool: """Return true if the device is recording.""" - return self.device.status.get(DPCode.RECORD_SWITCH, False) + if (status := self._read_wrapper(self._recording_status)) is not None: + return status + return False @property def motion_detection_enabled(self) -> bool: """Return the camera motion detection status.""" - return self.device.status.get(DPCode.MOTION_SWITCH, False) + if (status := self._read_wrapper(self._motion_detection_switch)) is not None: + return status + return False async def stream_source(self) -> str | None: """Return the source of the stream.""" @@ -95,10 +116,10 @@ async def async_camera_image( height=height, ) - def enable_motion_detection(self) -> None: + async def async_enable_motion_detection(self) -> None: """Enable motion detection in the camera.""" - self._send_command([{"code": DPCode.MOTION_SWITCH, "value": True}]) + await self._async_send_dpcode_update(self._motion_detection_switch, True) - def disable_motion_detection(self) -> None: + async def async_disable_motion_detection(self) -> None: """Disable motion detection in camera.""" - self._send_command([{"code": DPCode.MOTION_SWITCH, "value": False}]) + await self._async_send_dpcode_update(self._motion_detection_switch, False) diff --git a/homeassistant/components/tuya/const.py b/homeassistant/components/tuya/const.py index 436d396c76b7f..edd807c980fcc 100644 --- a/homeassistant/components/tuya/const.py +++ b/homeassistant/components/tuya/const.py @@ -709,6 +709,7 @@ class DPCode(StrEnum): DEW_POINT_TEMP = "dew_point_temp" DISINFECTION = "disinfection" DO_NOT_DISTURB = "do_not_disturb" + DOORBELL_PIC = "doorbell_pic" DOORCONTACT_STATE = "doorcontact_state" # Status of door window sensor DOORCONTACT_STATE_2 = "doorcontact_state_2" DOORCONTACT_STATE_3 = "doorcontact_state_3" diff --git a/homeassistant/components/tuya/diagnostics.py b/homeassistant/components/tuya/diagnostics.py index 85fce1f9ba82b..770686d1b5530 100644 --- a/homeassistant/components/tuya/diagnostics.py +++ b/homeassistant/components/tuya/diagnostics.py @@ -15,6 +15,13 @@ from . import TuyaConfigEntry from .const import DOMAIN, DPCode +_REDACTED_DPCODES = { + DPCode.ALARM_MESSAGE, + DPCode.ALARM_MSG, + DPCode.DOORBELL_PIC, + DPCode.MOVEMENT_DETECT_PIC, +} + async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: TuyaConfigEntry @@ -95,7 +102,7 @@ def _async_device_as_dict( # Gather Tuya states for dpcode, value in device.status.items(): # These statuses may contain sensitive information, redact these.. - if dpcode in {DPCode.ALARM_MESSAGE, DPCode.MOVEMENT_DETECT_PIC}: + if dpcode in _REDACTED_DPCODES: data["status"][dpcode] = REDACTED continue diff --git a/homeassistant/components/tuya/entity.py b/homeassistant/components/tuya/entity.py index 42dbbf427fdb7..59eb37b8db0e4 100644 --- a/homeassistant/components/tuya/entity.py +++ b/homeassistant/components/tuya/entity.py @@ -66,10 +66,18 @@ def _send_command(self, commands: list[dict[str, Any]]) -> None: LOGGER.debug("Sending commands for device %s: %s", self.device.id, commands) self.device_manager.send_commands(self.device.id, commands) + def _read_wrapper(self, dpcode_wrapper: DPCodeWrapper | None) -> Any | None: + """Read the wrapper device status.""" + if dpcode_wrapper is None: + return None + return dpcode_wrapper.read_device_status(self.device) + async def _async_send_dpcode_update( - self, dpcode_wrapper: DPCodeWrapper, value: Any + self, dpcode_wrapper: DPCodeWrapper | None, value: Any ) -> None: """Send command to the device.""" + if dpcode_wrapper is None: + return await self.hass.async_add_executor_job( self._send_command, [dpcode_wrapper.get_update_command(self.device, value)], diff --git a/homeassistant/components/tuya/models.py b/homeassistant/components/tuya/models.py index 6327f3b46d1b2..94cf0d1ab8b89 100644 --- a/homeassistant/components/tuya/models.py +++ b/homeassistant/components/tuya/models.py @@ -39,7 +39,6 @@ class IntegerTypeData(TypeInformation): scale: float step: float unit: str | None = None - type: str | None = None @property def max_scaled(self) -> float: @@ -62,7 +61,7 @@ def scale_value(self, value: float) -> float: def scale_value_back(self, value: float) -> int: """Return raw value for scaled.""" - return int(value * (10**self.scale)) + return round(value * (10**self.scale)) def remap_value_to( self, @@ -97,7 +96,6 @@ def from_json(cls, dpcode: DPCode, data: str) -> Self | None: scale=float(parsed["scale"]), step=max(float(parsed["step"]), 1), unit=parsed.get("unit"), - type=parsed.get("type"), ) diff --git a/homeassistant/components/velux/cover.py b/homeassistant/components/velux/cover.py index 5a371d6061ab6..1fb3de56ce68d 100644 --- a/homeassistant/components/velux/cover.py +++ b/homeassistant/components/velux/cover.py @@ -56,37 +56,32 @@ class VeluxCover(VeluxEntity, CoverEntity): def __init__(self, node: OpeningDevice, config_entry_id: str) -> None: """Initialize VeluxCover.""" super().__init__(node, config_entry_id) + # Features common to all covers + self._attr_supported_features = ( + CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + | CoverEntityFeature.STOP + ) # Window is the default device class for covers self._attr_device_class = CoverDeviceClass.WINDOW if isinstance(node, Awning): self._attr_device_class = CoverDeviceClass.AWNING - if isinstance(node, Blind): - self._attr_device_class = CoverDeviceClass.BLIND - self._is_blind = True if isinstance(node, GarageDoor): self._attr_device_class = CoverDeviceClass.GARAGE if isinstance(node, Gate): self._attr_device_class = CoverDeviceClass.GATE if isinstance(node, RollerShutter): self._attr_device_class = CoverDeviceClass.SHUTTER - - @property - def supported_features(self) -> CoverEntityFeature: - """Flag supported features.""" - supported_features = ( - CoverEntityFeature.OPEN - | CoverEntityFeature.CLOSE - | CoverEntityFeature.SET_POSITION - | CoverEntityFeature.STOP - ) - if self.current_cover_tilt_position is not None: - supported_features |= ( + if isinstance(node, Blind): + self._attr_device_class = CoverDeviceClass.BLIND + self._is_blind = True + self._attr_supported_features |= ( CoverEntityFeature.OPEN_TILT | CoverEntityFeature.CLOSE_TILT | CoverEntityFeature.SET_TILT_POSITION | CoverEntityFeature.STOP_TILT ) - return supported_features @property def current_cover_position(self) -> int: diff --git a/homeassistant/components/vicare/sensor.py b/homeassistant/components/vicare/sensor.py index c1f672a44791c..a50f9fdc4f172 100644 --- a/homeassistant/components/vicare/sensor.py +++ b/homeassistant/components/vicare/sensor.py @@ -59,7 +59,7 @@ get_burners, get_circuits, get_compressors, - get_condensors, + get_condensers, get_device_serial, get_evaporators, is_supported, @@ -1237,10 +1237,10 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM ), ) -CONDENSOR_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( +CONDENSER_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ViCareSensorEntityDescription( - key="condensor_liquid_temperature", - translation_key="condensor_liquid_temperature", + key="condenser_liquid_temperature", + translation_key="condenser_liquid_temperature", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getCondensorLiquidTemperature(), @@ -1248,8 +1248,8 @@ class ViCareSensorEntityDescription(SensorEntityDescription, ViCareRequiredKeysM entity_registry_enabled_default=False, ), ViCareSensorEntityDescription( - key="condensor_subcooling_temperature", - translation_key="condensor_subcooling_temperature", + key="condenser_subcooling_temperature", + translation_key="condenser_subcooling_temperature", device_class=SensorDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, value_getter=lambda api: api.getCondensorSubcoolingTemperature(), @@ -1303,7 +1303,7 @@ def _build_entities( (get_circuits(device.api), CIRCUIT_SENSORS), (get_burners(device.api), BURNER_SENSORS), (get_compressors(device.api), COMPRESSOR_SENSORS), - (get_condensors(device.api), CONDENSOR_SENSORS), + (get_condensers(device.api), CONDENSER_SENSORS), (get_evaporators(device.api), EVAPORATOR_SENSORS), ): entities.extend( diff --git a/homeassistant/components/vicare/strings.json b/homeassistant/components/vicare/strings.json index b6fc180010992..effec9f2a6a38 100644 --- a/homeassistant/components/vicare/strings.json +++ b/homeassistant/components/vicare/strings.json @@ -244,11 +244,11 @@ "compressor_starts": { "name": "Compressor starts" }, - "condensor_liquid_temperature": { - "name": "Condensor liquid temperature" + "condenser_liquid_temperature": { + "name": "Condenser liquid temperature" }, - "condensor_subcooling_temperature": { - "name": "Condensor subcooling temperature" + "condenser_subcooling_temperature": { + "name": "Condenser subcooling temperature" }, "dhw_storage_bottom_temperature": { "name": "DHW storage bottom temperature" diff --git a/homeassistant/components/vicare/utils.py b/homeassistant/components/vicare/utils.py index f8d8b7b3c2e68..afde5a0c6b962 100644 --- a/homeassistant/components/vicare/utils.py +++ b/homeassistant/components/vicare/utils.py @@ -130,14 +130,14 @@ def get_compressors(device: PyViCareDevice) -> list[PyViCareHeatingDeviceCompone return [] -def get_condensors(device: PyViCareDevice) -> list[PyViCareHeatingDeviceComponent]: - """Return the list of condensors.""" +def get_condensers(device: PyViCareDevice) -> list[PyViCareHeatingDeviceComponent]: + """Return the list of condensers.""" try: return device.condensors except PyViCareNotSupportedFeatureError: - _LOGGER.debug("No condensors found") + _LOGGER.debug("No condensers found") except AttributeError as error: - _LOGGER.debug("No condensors found: %s", error) + _LOGGER.debug("No condensers found: %s", error) return [] diff --git a/homeassistant/components/xbox/media_player.py b/homeassistant/components/xbox/media_player.py index 73bee4ea901f3..410dcabba3915 100644 --- a/homeassistant/components/xbox/media_player.py +++ b/homeassistant/components/xbox/media_player.py @@ -99,6 +99,11 @@ def media_content_type(self) -> MediaType: return MediaType.GAME return MediaType.APP + @property + def media_content_id(self) -> str | None: + """Content ID of current playing media.""" + return self.data.app_details.product_id if self.data.app_details else None + @property def media_title(self) -> str | None: """Title of current playing media.""" diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 128c125ab5d50..64cbfec6da91f 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -5689,7 +5689,7 @@ "integration_type": "hub", "config_flow": true, "iot_class": "local_push", - "name": "RuuviTag BLE" + "name": "Ruuvi BLE" } } }, diff --git a/requirements_all.txt b/requirements_all.txt index 6606b161041ad..3dcc54270a971 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2128,7 +2128,7 @@ pykwb==0.0.8 pylacrosse==0.4 # homeassistant.components.lamarzocco -pylamarzocco==2.1.2 +pylamarzocco==2.1.3 # homeassistant.components.lastfm pylast==5.1.0 @@ -2380,7 +2380,7 @@ pysmappee==0.2.29 pysmarlaapi==0.9.2 # homeassistant.components.smartthings -pysmartthings==3.3.2 +pysmartthings==3.3.3 # homeassistant.components.smarty pysmarty2==0.10.3 @@ -2437,7 +2437,7 @@ python-awair==0.2.4 python-blockchain-api==0.0.2 # homeassistant.components.bsblan -python-bsblan==3.1.0 +python-bsblan==3.1.1 # homeassistant.components.citybikes python-citybikes==0.3.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c61a8322a73e4..6602be40ec8f6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1772,7 +1772,7 @@ pykrakenapi==0.1.8 pykulersky==0.5.8 # homeassistant.components.lamarzocco -pylamarzocco==2.1.2 +pylamarzocco==2.1.3 # homeassistant.components.lastfm pylast==5.1.0 @@ -1982,7 +1982,7 @@ pysmappee==0.2.29 pysmarlaapi==0.9.2 # homeassistant.components.smartthings -pysmartthings==3.3.2 +pysmartthings==3.3.3 # homeassistant.components.smarty pysmarty2==0.10.3 @@ -2033,7 +2033,7 @@ python-MotionMount==2.3.0 python-awair==0.2.4 # homeassistant.components.bsblan -python-bsblan==3.1.0 +python-bsblan==3.1.1 # homeassistant.components.ecobee python-ecobee-api==0.3.2 diff --git a/script/hassfest/quality_scale.py b/script/hassfest/quality_scale.py index 09007f380426d..c605fbe0ff211 100644 --- a/script/hassfest/quality_scale.py +++ b/script/hassfest/quality_scale.py @@ -331,7 +331,6 @@ class Rule: "elv", "elvia", "emby", - "emoncms", "emoncms_history", "emonitor", "emulated_hue", diff --git a/tests/components/asuswrt/test_sensor.py b/tests/components/asuswrt/test_sensor.py index f220b233b1daf..6fa503c8e97ff 100644 --- a/tests/components/asuswrt/test_sensor.py +++ b/tests/components/asuswrt/test_sensor.py @@ -616,12 +616,10 @@ async def test_decorator_errors( config_entry.add_to_hass(hass) mock_available_temps[1] = True - connect_legacy.return_value.async_get_bytes_total.return_value = "bad_response" - connect_legacy.return_value.async_get_current_transfer_rates.return_value = ( - "bad_response" - ) - connect_legacy.return_value.async_get_temperature.return_value = "bad_response" - connect_legacy.return_value.async_get_loadavg.return_value = "bad_response" + connect_legacy.return_value.async_get_bytes_total.return_value = None + connect_legacy.return_value.async_get_current_transfer_rates.return_value = None + connect_legacy.return_value.async_get_temperature.return_value = None + connect_legacy.return_value.async_get_loadavg.return_value = None # initial devices setup assert await hass.config_entries.async_setup(config_entry.entry_id) @@ -631,7 +629,5 @@ async def test_decorator_errors( await hass.async_block_till_done() for sensor_name in sensors: - assert ( - hass.states.get(f"{sensor_prefix}_{slugify(sensor_name)}").state - == STATE_UNAVAILABLE - ) + sensor = hass.states.get(f"{sensor_prefix}_{slugify(sensor_name)}") + assert sensor and sensor.state == STATE_UNAVAILABLE diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index a665b4328b7df..087634c5173eb 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -47,6 +47,9 @@ async def test_constructor_loads_info_from_config(hass: HomeAssistant) -> None: "accounts_server": "test-acounts-server", "acme_server": "test-acme-server", "remotestate_server": "test-remotestate-server", + "discovery_service_actions": { + "lorem_ipsum": "https://lorem.ipsum/test-url" + }, }, }, ) @@ -63,6 +66,10 @@ async def test_constructor_loads_info_from_config(hass: HomeAssistant) -> None: assert cl.acme_server == "test-acme-server" assert cl.api_server == "test-api-server" assert cl.remotestate_server == "test-remotestate-server" + assert ( + cl.service_discovery._action_overrides["lorem_ipsum"] + == "https://lorem.ipsum/test-url" + ) @pytest.mark.usefixtures("mock_cloud_fixture") diff --git a/tests/components/enphase_envoy/snapshots/test_diagnostics.ambr b/tests/components/enphase_envoy/snapshots/test_diagnostics.ambr index ca6c502d3be35..96866d76e2f6b 100644 --- a/tests/components/enphase_envoy/snapshots/test_diagnostics.ambr +++ b/tests/components/enphase_envoy/snapshots/test_diagnostics.ambr @@ -825,6 +825,10 @@ 'ctmeter_production_phases': None, 'ctmeter_storage': None, 'ctmeter_storage_phases': None, + 'ctmeters': dict({ + }), + 'ctmeters_phases': dict({ + }), 'dry_contact_settings': dict({ }), 'dry_contact_status': dict({ @@ -854,6 +858,8 @@ 'active_phasecount': 0, 'ct_consumption_meter': None, 'ct_count': 0, + 'ct_meters': list([ + ]), 'ct_production_meter': None, 'ct_storage_meter': None, 'envoy_firmware': '7.6.175', @@ -1700,6 +1706,10 @@ 'ctmeter_production_phases': None, 'ctmeter_storage': None, 'ctmeter_storage_phases': None, + 'ctmeters': dict({ + }), + 'ctmeters_phases': dict({ + }), 'dry_contact_settings': dict({ }), 'dry_contact_status': dict({ @@ -1729,6 +1739,8 @@ 'active_phasecount': 0, 'ct_consumption_meter': None, 'ct_count': 0, + 'ct_meters': list([ + ]), 'ct_production_meter': None, 'ct_storage_meter': None, 'envoy_firmware': '7.6.175', @@ -2619,6 +2631,10 @@ 'ctmeter_production_phases': None, 'ctmeter_storage': None, 'ctmeter_storage_phases': None, + 'ctmeters': dict({ + }), + 'ctmeters_phases': dict({ + }), 'dry_contact_settings': dict({ }), 'dry_contact_status': dict({ @@ -2648,6 +2664,8 @@ 'active_phasecount': 0, 'ct_consumption_meter': None, 'ct_count': 0, + 'ct_meters': list([ + ]), 'ct_production_meter': None, 'ct_storage_meter': None, 'envoy_firmware': '7.6.175', @@ -2733,7 +2751,7 @@ }), }) # --- -# name: test_entry_diagnostics_with_interface_information +# name: test_entry_diagnostics_with_interface_information[envoy] dict({ 'config_entry': dict({ 'data': dict({ @@ -3563,6 +3581,10 @@ 'ctmeter_production_phases': None, 'ctmeter_storage': None, 'ctmeter_storage_phases': None, + 'ctmeters': dict({ + }), + 'ctmeters_phases': dict({ + }), 'dry_contact_settings': dict({ }), 'dry_contact_status': dict({ @@ -3598,6 +3620,8 @@ 'active_phasecount': 0, 'ct_consumption_meter': None, 'ct_count': 0, + 'ct_meters': list([ + ]), 'ct_production_meter': None, 'ct_storage_meter': None, 'envoy_firmware': '7.6.175', @@ -3617,3 +3641,9060 @@ }), }) # --- +# name: test_entry_diagnostics_with_interface_information[envoy_metered_batt_relay] + dict({ + 'config_entry': dict({ + 'data': dict({ + 'host': '1.1.1.1', + 'name': '**REDACTED**', + 'password': '**REDACTED**', + 'token': '**REDACTED**', + 'username': '**REDACTED**', + }), + 'disabled_by': None, + 'discovery_keys': dict({ + }), + 'domain': 'enphase_envoy', + 'entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'minor_version': 1, + 'options': dict({ + }), + 'pref_disable_new_entities': False, + 'pref_disable_polling': False, + 'source': 'user', + 'subentries': list([ + ]), + 'title': '**REDACTED**', + 'unique_id': '**REDACTED**', + 'version': 1, + }), + 'envoy_entities_by_device': list([ + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': '<>56789', + 'identifiers': list([ + list([ + 'enphase_envoy', + '<>', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Envoy, phases: 3, phase mode: split, net-consumption CT, production CT, storage CT', + 'model_id': None, + 'name': 'Envoy <>', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': '<>', + 'sw_version': '7.1.2', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_production', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power production', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_production', + 'unique_id': '<>_production', + 'unit_of_measurement': 'kW', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'power', + 'friendly_name': 'Envoy <> Current power production', + 'state_class': 'measurement', + 'unit_of_measurement': 'kW', + }), + 'entity_id': 'sensor.envoy_<>_current_power_production', + 'state': '1.234', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_today', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production today', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_production', + 'unique_id': '<>_daily_production', + 'unit_of_measurement': 'kWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Energy production today', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'kWh', + }), + 'entity_id': 'sensor.envoy_<>_energy_production_today', + 'state': '1.234', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_last_seven_days', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production last seven days', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_production', + 'unique_id': '<>_seven_days_production', + 'unit_of_measurement': 'kWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Energy production last seven days', + 'unit_of_measurement': 'kWh', + }), + 'entity_id': 'sensor.envoy_<>_energy_production_last_seven_days', + 'state': '1.234', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_production', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy production', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_production', + 'unique_id': '<>_lifetime_production', + 'unit_of_measurement': 'MWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Lifetime energy production', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'MWh', + }), + 'entity_id': 'sensor.envoy_<>_lifetime_energy_production', + 'state': '0.00<>', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_consumption', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power consumption', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_consumption', + 'unique_id': '<>_consumption', + 'unit_of_measurement': 'kW', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'power', + 'friendly_name': 'Envoy <> Current power consumption', + 'state_class': 'measurement', + 'unit_of_measurement': 'kW', + }), + 'entity_id': 'sensor.envoy_<>_current_power_consumption', + 'state': '1.234', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_today', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 2, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption today', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_consumption', + 'unique_id': '<>_daily_consumption', + 'unit_of_measurement': 'kWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Energy consumption today', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'kWh', + }), + 'entity_id': 'sensor.envoy_<>_energy_consumption_today', + 'state': '1.234', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_last_seven_days', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption last seven days', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_consumption', + 'unique_id': '<>_seven_days_consumption', + 'unit_of_measurement': 'kWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Energy consumption last seven days', + 'unit_of_measurement': 'kWh', + }), + 'entity_id': 'sensor.envoy_<>_energy_consumption_last_seven_days', + 'state': '1.234', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_consumption', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy consumption', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_consumption', + 'unique_id': '<>_lifetime_consumption', + 'unit_of_measurement': 'MWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Lifetime energy consumption', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'MWh', + }), + 'entity_id': 'sensor.envoy_<>_lifetime_energy_consumption', + 'state': '0.00<>', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_balanced_net_power_consumption', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Balanced net power consumption', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'balanced_net_consumption', + 'unique_id': '<>_balanced_net_consumption', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_balanced_net_energy_consumption', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime balanced net energy consumption', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_balanced_net_consumption', + 'unique_id': '<>_lifetime_balanced_net_consumption', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_production_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power production l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_production_phase', + 'unique_id': '<>_production_l1', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_today_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production today l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_production_phase', + 'unique_id': '<>_daily_production_l1', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_last_seven_days_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production last seven days l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_production_phase', + 'unique_id': '<>_seven_days_production_l1', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_production_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy production l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_production_phase', + 'unique_id': '<>_lifetime_production_l1', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_production_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power production l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_production_phase', + 'unique_id': '<>_production_l2', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_today_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production today l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_production_phase', + 'unique_id': '<>_daily_production_l2', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_last_seven_days_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production last seven days l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_production_phase', + 'unique_id': '<>_seven_days_production_l2', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_production_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy production l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_production_phase', + 'unique_id': '<>_lifetime_production_l2', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_production_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power production l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_production_phase', + 'unique_id': '<>_production_l3', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_today_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production today l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_production_phase', + 'unique_id': '<>_daily_production_l3', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_production_last_seven_days_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production last seven days l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_production_phase', + 'unique_id': '<>_seven_days_production_l3', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_production_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy production l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_production_phase', + 'unique_id': '<>_lifetime_production_l3', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_consumption_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power consumption l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_consumption_phase', + 'unique_id': '<>_consumption_l1', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_today_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption today l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_consumption_phase', + 'unique_id': '<>_daily_consumption_l1', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_last_seven_days_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption last seven days l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_consumption_phase', + 'unique_id': '<>_seven_days_consumption_l1', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_consumption_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy consumption l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_consumption_phase', + 'unique_id': '<>_lifetime_consumption_l1', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_consumption_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power consumption l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_consumption_phase', + 'unique_id': '<>_consumption_l2', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_today_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption today l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_consumption_phase', + 'unique_id': '<>_daily_consumption_l2', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_last_seven_days_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption last seven days l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_consumption_phase', + 'unique_id': '<>_seven_days_consumption_l2', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_consumption_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy consumption l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_consumption_phase', + 'unique_id': '<>_lifetime_consumption_l2', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_power_consumption_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current power consumption l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'current_power_consumption_phase', + 'unique_id': '<>_consumption_l3', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_today_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption today l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'daily_consumption_phase', + 'unique_id': '<>_daily_consumption_l3', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_energy_consumption_last_seven_days_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy consumption last seven days l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'seven_days_consumption_phase', + 'unique_id': '<>_seven_days_consumption_l3', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_energy_consumption_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy consumption l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_consumption_phase', + 'unique_id': '<>_lifetime_consumption_l3', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_balanced_net_power_consumption_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Balanced net power consumption l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'balanced_net_consumption_phase', + 'unique_id': '<>_balanced_net_consumption_l1', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_balanced_net_energy_consumption_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime balanced net energy consumption l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_balanced_net_consumption_phase', + 'unique_id': '<>_lifetime_balanced_net_consumption_l1', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_balanced_net_power_consumption_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Balanced net power consumption l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'balanced_net_consumption_phase', + 'unique_id': '<>_balanced_net_consumption_l2', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_balanced_net_energy_consumption_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime balanced net energy consumption l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_balanced_net_consumption_phase', + 'unique_id': '<>_lifetime_balanced_net_consumption_l2', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_balanced_net_power_consumption_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Balanced net power consumption l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'balanced_net_consumption_phase', + 'unique_id': '<>_balanced_net_consumption_l3', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_balanced_net_energy_consumption_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime balanced net energy consumption l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_balanced_net_consumption_phase', + 'unique_id': '<>_lifetime_balanced_net_consumption_l3', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_consumption', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy consumption', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_consumption', + 'unique_id': '<>_lifetime_net_consumption', + 'unit_of_measurement': 'MWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Lifetime net energy consumption', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'MWh', + }), + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_consumption', + 'state': '0.02<>', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_discharged', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy discharged', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_discharged', + 'unique_id': '<>_lifetime_battery_discharged', + 'unit_of_measurement': 'MWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Lifetime battery energy discharged', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'MWh', + }), + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_discharged', + 'state': '0.03<>', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_production', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy production', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_production', + 'unique_id': '<>_lifetime_net_production', + 'unit_of_measurement': 'MWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Lifetime net energy production', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'MWh', + }), + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_production', + 'state': '0.022345', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_charged', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy charged', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_charged', + 'unique_id': '<>_lifetime_battery_charged', + 'unit_of_measurement': 'MWh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Lifetime battery energy charged', + 'state_class': 'total_increasing', + 'unit_of_measurement': 'MWh', + }), + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_charged', + 'state': '0.032345', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_net_power_consumption', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current net power consumption', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_consumption', + 'unique_id': '<>_net_consumption', + 'unit_of_measurement': 'kW', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'power', + 'friendly_name': 'Envoy <> Current net power consumption', + 'state_class': 'measurement', + 'unit_of_measurement': 'kW', + }), + 'entity_id': 'sensor.envoy_<>_current_net_power_consumption', + 'state': '0.101', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_battery_discharge', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 3, + }), + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current battery discharge', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery_discharge', + 'unique_id': '<>_battery_discharge', + 'unit_of_measurement': 'kW', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'power', + 'friendly_name': 'Envoy <> Current battery discharge', + 'state_class': 'measurement', + 'unit_of_measurement': 'kW', + }), + 'entity_id': 'sensor.envoy_<>_current_battery_discharge', + 'state': '0.103', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_net_consumption_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency net consumption CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_frequency', + 'unique_id': '<>_frequency', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_production_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency production CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_frequency', + 'unique_id': '<>_production_ct_frequency', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_storage_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency storage CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_frequency', + 'unique_id': '<>_storage_ct_frequency', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_net_consumption_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage net consumption CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_voltage', + 'unique_id': '<>_voltage', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_production_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage production CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_voltage', + 'unique_id': '<>_production_ct_voltage', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_storage_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage storage CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_voltage', + 'unique_id': '<>_storage_voltage', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_net_consumption_ct_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Net consumption CT current', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_current', + 'unique_id': '<>_net_ct_current', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_production_ct_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Production CT current', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_current', + 'unique_id': '<>_production_ct_current', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_storage_ct_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Storage CT current', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_current', + 'unique_id': '<>_storage_ct_current', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_net_consumption_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor net consumption CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_powerfactor', + 'unique_id': '<>_net_ct_powerfactor', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_production_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor production CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_powerfactor', + 'unique_id': '<>_production_ct_powerfactor', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_storage_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor storage CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_powerfactor', + 'unique_id': '<>_storage_ct_powerfactor', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_net_consumption_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status net consumption CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_metering_status', + 'unique_id': '<>_net_consumption_ct_metering_status', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_production_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status production CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_metering_status', + 'unique_id': '<>_production_ct_metering_status', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_storage_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status storage CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_metering_status', + 'unique_id': '<>_storage_ct_metering_status', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_net_consumption_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active net consumption CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_status_flags', + 'unique_id': '<>_net_consumption_ct_status_flags', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_production_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active production CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_status_flags', + 'unique_id': '<>_production_ct_status_flags', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_storage_ct', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active storage CT', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_status_flags', + 'unique_id': '<>_storage_ct_status_flags', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_consumption_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy consumption l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_consumption_phase', + 'unique_id': '<>_lifetime_net_consumption_l1', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_discharged_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy discharged l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_discharged_phase', + 'unique_id': '<>_lifetime_battery_discharged_l1', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_production_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy production l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_production_phase', + 'unique_id': '<>_lifetime_net_production_l1', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_charged_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy charged l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_charged_phase', + 'unique_id': '<>_lifetime_battery_charged_l1', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_net_power_consumption_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current net power consumption l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_consumption_phase', + 'unique_id': '<>_net_consumption_l1', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_battery_discharge_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current battery discharge l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery_discharge_phase', + 'unique_id': '<>_battery_discharge_l1', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_net_consumption_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency net consumption CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_frequency_phase', + 'unique_id': '<>_frequency_l1', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_production_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency production CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_frequency_phase', + 'unique_id': '<>_production_ct_frequency_l1', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_storage_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency storage CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_frequency_phase', + 'unique_id': '<>_storage_ct_frequency_l1', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_net_consumption_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage net consumption CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_voltage_phase', + 'unique_id': '<>_voltage_l1', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_production_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage production CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_voltage_phase', + 'unique_id': '<>_production_ct_voltage_l1', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_storage_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage storage CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_voltage_phase', + 'unique_id': '<>_storage_voltage_l1', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_net_consumption_ct_current_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Net consumption CT current l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_current_phase', + 'unique_id': '<>_net_ct_current_l1', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_production_ct_current_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Production CT current l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_current_phase', + 'unique_id': '<>_production_ct_current_l1', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_storage_ct_current_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Storage CT current l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_current_phase', + 'unique_id': '<>_storage_ct_current_l1', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_net_consumption_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor net consumption CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_powerfactor_phase', + 'unique_id': '<>_net_ct_powerfactor_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_production_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor production CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_powerfactor_phase', + 'unique_id': '<>_production_ct_powerfactor_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_storage_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor storage CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_powerfactor_phase', + 'unique_id': '<>_storage_ct_powerfactor_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_net_consumption_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status net consumption CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_metering_status_phase', + 'unique_id': '<>_net_consumption_ct_metering_status_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_production_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status production CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_metering_status_phase', + 'unique_id': '<>_production_ct_metering_status_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_storage_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status storage CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_metering_status_phase', + 'unique_id': '<>_storage_ct_metering_status_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_net_consumption_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active net consumption CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_status_flags_phase', + 'unique_id': '<>_net_consumption_ct_status_flags_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_production_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active production CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_status_flags_phase', + 'unique_id': '<>_production_ct_status_flags_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_storage_ct_l1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active storage CT l1', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_status_flags_phase', + 'unique_id': '<>_storage_ct_status_flags_l1', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_consumption_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy consumption l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_consumption_phase', + 'unique_id': '<>_lifetime_net_consumption_l2', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_discharged_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy discharged l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_discharged_phase', + 'unique_id': '<>_lifetime_battery_discharged_l2', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_production_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy production l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_production_phase', + 'unique_id': '<>_lifetime_net_production_l2', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_charged_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy charged l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_charged_phase', + 'unique_id': '<>_lifetime_battery_charged_l2', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_net_power_consumption_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current net power consumption l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_consumption_phase', + 'unique_id': '<>_net_consumption_l2', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_battery_discharge_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current battery discharge l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery_discharge_phase', + 'unique_id': '<>_battery_discharge_l2', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_net_consumption_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency net consumption CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_frequency_phase', + 'unique_id': '<>_frequency_l2', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_production_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency production CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_frequency_phase', + 'unique_id': '<>_production_ct_frequency_l2', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_storage_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency storage CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_frequency_phase', + 'unique_id': '<>_storage_ct_frequency_l2', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_net_consumption_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage net consumption CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_voltage_phase', + 'unique_id': '<>_voltage_l2', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_production_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage production CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_voltage_phase', + 'unique_id': '<>_production_ct_voltage_l2', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_storage_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage storage CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_voltage_phase', + 'unique_id': '<>_storage_voltage_l2', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_net_consumption_ct_current_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Net consumption CT current l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_current_phase', + 'unique_id': '<>_net_ct_current_l2', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_production_ct_current_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Production CT current l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_current_phase', + 'unique_id': '<>_production_ct_current_l2', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_storage_ct_current_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Storage CT current l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_current_phase', + 'unique_id': '<>_storage_ct_current_l2', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_net_consumption_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor net consumption CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_powerfactor_phase', + 'unique_id': '<>_net_ct_powerfactor_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_production_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor production CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_powerfactor_phase', + 'unique_id': '<>_production_ct_powerfactor_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_storage_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor storage CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_powerfactor_phase', + 'unique_id': '<>_storage_ct_powerfactor_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_net_consumption_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status net consumption CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_metering_status_phase', + 'unique_id': '<>_net_consumption_ct_metering_status_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_production_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status production CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_metering_status_phase', + 'unique_id': '<>_production_ct_metering_status_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_storage_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status storage CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_metering_status_phase', + 'unique_id': '<>_storage_ct_metering_status_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_net_consumption_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active net consumption CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_status_flags_phase', + 'unique_id': '<>_net_consumption_ct_status_flags_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_production_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active production CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_status_flags_phase', + 'unique_id': '<>_production_ct_status_flags_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_storage_ct_l2', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active storage CT l2', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_status_flags_phase', + 'unique_id': '<>_storage_ct_status_flags_l2', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_consumption_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy consumption l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_consumption_phase', + 'unique_id': '<>_lifetime_net_consumption_l3', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_discharged_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy discharged l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_discharged_phase', + 'unique_id': '<>_lifetime_battery_discharged_l3', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_net_energy_production_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime net energy production l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_net_production_phase', + 'unique_id': '<>_lifetime_net_production_l3', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_lifetime_battery_energy_charged_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'MWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime battery energy charged l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_battery_charged_phase', + 'unique_id': '<>_lifetime_battery_charged_l3', + 'unit_of_measurement': 'MWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_net_power_consumption_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current net power consumption l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_consumption_phase', + 'unique_id': '<>_net_consumption_l3', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_current_battery_discharge_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kW', + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Current battery discharge l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'battery_discharge_phase', + 'unique_id': '<>_battery_discharge_l3', + 'unit_of_measurement': 'kW', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_net_consumption_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency net consumption CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_frequency_phase', + 'unique_id': '<>_frequency_l3', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_production_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency production CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_frequency_phase', + 'unique_id': '<>_production_ct_frequency_l3', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_frequency_storage_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency storage CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_frequency_phase', + 'unique_id': '<>_storage_ct_frequency_l3', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_net_consumption_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage net consumption CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_voltage_phase', + 'unique_id': '<>_voltage_l3', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_production_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage production CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_voltage_phase', + 'unique_id': '<>_production_ct_voltage_l3', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_voltage_storage_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'V', + }), + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'Voltage storage CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_voltage_phase', + 'unique_id': '<>_storage_voltage_l3', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_net_consumption_ct_current_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Net consumption CT current l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_current_phase', + 'unique_id': '<>_net_ct_current_l3', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_production_ct_current_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Production CT current l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_current_phase', + 'unique_id': '<>_production_ct_current_l3', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_storage_ct_current_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'A', + }), + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'Storage CT current l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_current_phase', + 'unique_id': '<>_storage_ct_current_l3', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_net_consumption_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor net consumption CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_powerfactor_phase', + 'unique_id': '<>_net_ct_powerfactor_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_production_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor production CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_powerfactor_phase', + 'unique_id': '<>_production_ct_powerfactor_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_power_factor_storage_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power_factor', + 'original_icon': None, + 'original_name': 'Power factor storage CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_powerfactor_phase', + 'unique_id': '<>_storage_ct_powerfactor_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_net_consumption_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status net consumption CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_metering_status_phase', + 'unique_id': '<>_net_consumption_ct_metering_status_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_production_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status production CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_metering_status_phase', + 'unique_id': '<>_production_ct_metering_status_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'normal', + 'not-metering', + 'check-wiring', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_metering_status_storage_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'enum', + 'original_icon': None, + 'original_name': 'Metering status storage CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_metering_status_phase', + 'unique_id': '<>_storage_ct_metering_status_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_net_consumption_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active net consumption CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'net_ct_status_flags_phase', + 'unique_id': '<>_net_consumption_ct_status_flags_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_production_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active production CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'production_ct_status_flags_phase', + 'unique_id': '<>_production_ct_status_flags_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.envoy_<>_meter_status_flags_active_storage_ct_l3', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Meter status flags active storage CT l3', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_ct_status_flags_phase', + 'unique_id': '<>_storage_ct_status_flags_l3', + 'unit_of_measurement': None, + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '<>_battery_level', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Envoy <> Battery', + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.envoy_<>_battery', + 'state': '15', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_reserve_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Reserve battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'reserve_soc', + 'unique_id': '<>_reserve_soc', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Envoy <> Reserve battery level', + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.envoy_<>_reserve_battery_level', + 'state': '15', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_available_battery_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Available battery energy', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'available_energy', + 'unique_id': '<>_available_energy', + 'unit_of_measurement': 'Wh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Available battery energy', + 'unit_of_measurement': 'Wh', + }), + 'entity_id': 'sensor.envoy_<>_available_battery_energy', + 'state': '525', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_reserve_battery_energy', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Reserve battery energy', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'reserve_energy', + 'unique_id': '<>_reserve_energy', + 'unit_of_measurement': 'Wh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Reserve battery energy', + 'unit_of_measurement': 'Wh', + }), + 'entity_id': 'sensor.envoy_<>_reserve_battery_energy', + 'state': '526', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.envoy_<>_battery_capacity', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Battery capacity', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_capacity', + 'unique_id': '<>_max_capacity', + 'unit_of_measurement': 'Wh', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'energy', + 'friendly_name': 'Envoy <> Battery capacity', + 'unit_of_measurement': 'Wh', + }), + 'entity_id': 'sensor.envoy_<>_battery_capacity', + 'state': '3500', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + '<>56', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Encharge', + 'model_id': None, + 'name': 'Encharge <>56', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': '<>56', + 'sw_version': '2.6.5973_rel/22.11', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'binary_sensor.encharge_<>56_communicating', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'connectivity', + 'original_icon': None, + 'original_name': 'Communicating', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'communicating', + 'unique_id': '<>56_communicating', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'connectivity', + 'friendly_name': 'Encharge <>56 Communicating', + }), + 'entity_id': 'binary_sensor.encharge_<>56_communicating', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'binary_sensor.encharge_<>56_dc_switch', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'DC switch', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_switch', + 'unique_id': '<>56_dc_switch', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Encharge <>56 DC switch', + }), + 'entity_id': 'binary_sensor.encharge_<>56_dc_switch', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.encharge_<>56_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': 'temperature', + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '<>56_temperature', + 'unit_of_measurement': '°C', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Encharge <>56 Temperature', + 'unit_of_measurement': '°C', + }), + 'entity_id': 'sensor.encharge_<>56_temperature', + 'state': '29', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.encharge_<>56_last_reported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'timestamp', + 'original_icon': None, + 'original_name': 'Last reported', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_reported', + 'unique_id': '<>56_last_reported', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'timestamp', + 'friendly_name': 'Encharge <>56 Last reported', + }), + 'entity_id': 'sensor.encharge_<>56_last_reported', + 'state': '2023-09-26T23:04:07+00:00', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.encharge_<>56_battery', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Battery', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '<>56_soc', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Encharge <>56 Battery', + 'unit_of_measurement': '%', + }), + 'entity_id': 'sensor.encharge_<>56_battery', + 'state': '15', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.encharge_<>56_apparent_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': 'apparent_power', + 'original_icon': None, + 'original_name': 'Apparent power', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '<>56_apparent_power_mva', + 'unit_of_measurement': 'VA', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'apparent_power', + 'friendly_name': 'Encharge <>56 Apparent power', + 'unit_of_measurement': 'VA', + }), + 'entity_id': 'sensor.encharge_<>56_apparent_power', + 'state': '0.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.encharge_<>56_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Power', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '<>56_real_power_mw', + 'unit_of_measurement': 'W', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'power', + 'friendly_name': 'Encharge <>56 Power', + 'unit_of_measurement': 'W', + }), + 'entity_id': 'sensor.encharge_<>56_power', + 'state': '0.0', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + '654321', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Enpower', + 'model_id': None, + 'name': 'Enpower 654321', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': '654321', + 'sw_version': '1.2.2064_release/20.34', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'binary_sensor.enpower_654321_communicating', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'connectivity', + 'original_icon': None, + 'original_name': 'Communicating', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'communicating', + 'unique_id': '654321_communicating', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'connectivity', + 'friendly_name': 'Enpower 654321 Communicating', + }), + 'entity_id': 'binary_sensor.enpower_654321_communicating', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.enpower_654321_grid_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Grid status', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'grid_status', + 'unique_id': '654321_mains_oper_state', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Enpower 654321 Grid status', + }), + 'entity_id': 'binary_sensor.enpower_654321_grid_status', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': None, + 'entity_id': 'number.enpower_654321_reserve_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Reserve battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'reserve_soc', + 'unique_id': '654321_reserve_soc', + 'unit_of_measurement': '%', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'Enpower 654321 Reserve battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + 'unit_of_measurement': '%', + }), + 'entity_id': 'number.enpower_654321_reserve_battery_level', + 'state': '15.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'backup', + 'self_consumption', + 'savings', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.enpower_654321_storage_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Storage mode', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'storage_mode', + 'unique_id': '654321_storage_mode', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Enpower 654321 Storage mode', + 'options': list([ + 'backup', + 'self_consumption', + 'savings', + ]), + }), + 'entity_id': 'select.enpower_654321_storage_mode', + 'state': 'self_consumption', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.enpower_654321_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': 'temperature', + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '654321_temperature', + 'unit_of_measurement': '°C', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Enpower 654321 Temperature', + 'unit_of_measurement': '°C', + }), + 'entity_id': 'sensor.enpower_654321_temperature', + 'state': '26.1111111111111', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.enpower_654321_last_reported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'timestamp', + 'original_icon': None, + 'original_name': 'Last reported', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_reported', + 'unique_id': '654321_last_reported', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'timestamp', + 'friendly_name': 'Enpower 654321 Last reported', + }), + 'entity_id': 'sensor.enpower_654321_last_reported', + 'state': '2023-09-26T23:04:07+00:00', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.enpower_654321_grid_enabled', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Grid enabled', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'grid_enabled', + 'unique_id': '654321_mains_admin_state', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Enpower 654321 Grid enabled', + }), + 'entity_id': 'switch.enpower_654321_grid_enabled', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.enpower_654321_charge_from_grid', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Charge from grid', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'charge_from_grid', + 'unique_id': '654321_charge_from_grid', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Enpower 654321 Charge from grid', + }), + 'entity_id': 'switch.enpower_654321_charge_from_grid', + 'state': 'on', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + '482520020939', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'IQ Meter Collar', + 'model_id': None, + 'name': 'Collar 482520020939', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': '482520020939', + 'sw_version': '3.0.6-D0', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'binary_sensor.collar_482520020939_communicating', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'connectivity', + 'original_icon': None, + 'original_name': 'Communicating', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'communicating', + 'unique_id': '482520020939_communicating', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'connectivity', + 'friendly_name': 'Collar 482520020939 Communicating', + }), + 'entity_id': 'binary_sensor.collar_482520020939_communicating', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.collar_482520020939_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 1, + }), + }), + 'original_device_class': 'temperature', + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '482520020939_temperature', + 'unit_of_measurement': '°C', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'temperature', + 'friendly_name': 'Collar 482520020939 Temperature', + 'unit_of_measurement': '°C', + }), + 'entity_id': 'sensor.collar_482520020939_temperature', + 'state': '42', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.collar_482520020939_last_reported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'timestamp', + 'original_icon': None, + 'original_name': 'Last reported', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_reported', + 'unique_id': '482520020939_last_reported', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'timestamp', + 'friendly_name': 'Collar 482520020939 Last reported', + }), + 'entity_id': 'sensor.collar_482520020939_last_reported', + 'state': '2025-07-19T15:42:39+00:00', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.collar_482520020939_grid_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Grid status', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'grid_status', + 'unique_id': '482520020939_grid_state', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Collar 482520020939 Grid status', + }), + 'entity_id': 'sensor.collar_482520020939_grid_status', + 'state': 'on_grid', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.collar_482520020939_admin_state', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Admin state', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'admin_state', + 'unique_id': '482520020939_admin_state_str', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Collar 482520020939 Admin state', + }), + 'entity_id': 'sensor.collar_482520020939_admin_state', + 'state': 'on_grid', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.collar_482520020939_mid_state', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'MID state', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'mid_state', + 'unique_id': '482520020939_mid_state', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Collar 482520020939 MID state', + }), + 'entity_id': 'sensor.collar_482520020939_mid_state', + 'state': 'close', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + '482523040549', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'C6 COMBINER CONTROLLER', + 'model_id': None, + 'name': 'C6 Combiner 482523040549', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': '482523040549', + 'sw_version': '0.1.20-D1', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'binary_sensor.c6_combiner_482523040549_communicating', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'connectivity', + 'original_icon': None, + 'original_name': 'Communicating', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'communicating', + 'unique_id': '482523040549_communicating', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'connectivity', + 'friendly_name': 'C6 Combiner 482523040549 Communicating', + }), + 'entity_id': 'binary_sensor.c6_combiner_482523040549_communicating', + 'state': 'on', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.c6_combiner_482523040549_last_reported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'timestamp', + 'original_icon': None, + 'original_name': 'Last reported', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_reported', + 'unique_id': '482523040549_last_reported', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'timestamp', + 'friendly_name': 'C6 Combiner 482523040549 Last reported', + }), + 'entity_id': 'sensor.c6_combiner_482523040549_last_reported', + 'state': '2025-07-19T17:17:31+00:00', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + 'NC1', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Dry contact relay', + 'model_id': None, + 'name': 'NC1 Fixture', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': None, + 'sw_version': '1.2.2064_release/20.34', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': 'config', + 'entity_id': 'number.nc1_fixture_cutoff_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Cutoff battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cutoff_battery_level', + 'unique_id': '654321_relay_NC1_soc_low', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'NC1 Fixture Cutoff battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'entity_id': 'number.nc1_fixture_cutoff_battery_level', + 'state': '25.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': 'config', + 'entity_id': 'number.nc1_fixture_restore_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Restore battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'restore_battery_level', + 'unique_id': '654321_relay_NC1_soc_high', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'NC1 Fixture Restore battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'entity_id': 'number.nc1_fixture_restore_battery_level', + 'state': '70.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standard', + 'battery', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc1_fixture_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mode', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_mode', + 'unique_id': '654321_relay_NC1_mode', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC1 Fixture Mode', + 'options': list([ + 'standard', + 'battery', + ]), + }), + 'entity_id': 'select.nc1_fixture_mode', + 'state': 'standard', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc1_fixture_grid_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Grid action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_grid_action', + 'unique_id': '654321_relay_NC1_grid_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC1 Fixture Grid action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc1_fixture_grid_action', + 'state': 'not_powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc1_fixture_microgrid_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Microgrid action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_microgrid_action', + 'unique_id': '654321_relay_NC1_microgrid_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC1 Fixture Microgrid action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc1_fixture_microgrid_action', + 'state': 'not_powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc1_fixture_generator_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Generator action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_generator_action', + 'unique_id': '654321_relay_NC1_generator_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC1 Fixture Generator action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc1_fixture_generator_action', + 'state': 'not_powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.nc1_fixture', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_status', + 'unique_id': '654321_relay_NC1_relay_status', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC1 Fixture', + }), + 'entity_id': 'switch.nc1_fixture', + 'state': 'off', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + 'NC2', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Dry contact relay', + 'model_id': None, + 'name': 'NC2 Fixture', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': None, + 'sw_version': '1.2.2064_release/20.34', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': 'config', + 'entity_id': 'number.nc2_fixture_cutoff_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Cutoff battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cutoff_battery_level', + 'unique_id': '654321_relay_NC2_soc_low', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'NC2 Fixture Cutoff battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'entity_id': 'number.nc2_fixture_cutoff_battery_level', + 'state': '30.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': 'config', + 'entity_id': 'number.nc2_fixture_restore_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Restore battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'restore_battery_level', + 'unique_id': '654321_relay_NC2_soc_high', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'NC2 Fixture Restore battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'entity_id': 'number.nc2_fixture_restore_battery_level', + 'state': '70.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standard', + 'battery', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc2_fixture_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mode', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_mode', + 'unique_id': '654321_relay_NC2_mode', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC2 Fixture Mode', + 'options': list([ + 'standard', + 'battery', + ]), + }), + 'entity_id': 'select.nc2_fixture_mode', + 'state': 'standard', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc2_fixture_grid_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Grid action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_grid_action', + 'unique_id': '654321_relay_NC2_grid_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC2 Fixture Grid action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc2_fixture_grid_action', + 'state': 'powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc2_fixture_microgrid_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Microgrid action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_microgrid_action', + 'unique_id': '654321_relay_NC2_microgrid_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC2 Fixture Microgrid action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc2_fixture_microgrid_action', + 'state': 'not_powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc2_fixture_generator_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Generator action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_generator_action', + 'unique_id': '654321_relay_NC2_generator_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC2 Fixture Generator action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc2_fixture_generator_action', + 'state': 'not_powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.nc2_fixture', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_status', + 'unique_id': '654321_relay_NC2_relay_status', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC2 Fixture', + }), + 'entity_id': 'switch.nc2_fixture', + 'state': 'on', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + 'NC3', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Dry contact relay', + 'model_id': None, + 'name': 'NC3 Fixture', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': None, + 'sw_version': '1.2.2064_release/20.34', + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': 'config', + 'entity_id': 'number.nc3_fixture_cutoff_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Cutoff battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'cutoff_battery_level', + 'unique_id': '654321_relay_NC3_soc_low', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'NC3 Fixture Cutoff battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'entity_id': 'number.nc3_fixture_cutoff_battery_level', + 'state': '30.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'number', + 'entity_category': 'config', + 'entity_id': 'number.nc3_fixture_restore_battery_level', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'battery', + 'original_icon': None, + 'original_name': 'Restore battery level', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'restore_battery_level', + 'unique_id': '654321_relay_NC3_soc_high', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'battery', + 'friendly_name': 'NC3 Fixture Restore battery level', + 'max': 100.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + }), + 'entity_id': 'number.nc3_fixture_restore_battery_level', + 'state': '70.0', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'standard', + 'battery', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc3_fixture_mode', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Mode', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_mode', + 'unique_id': '654321_relay_NC3_mode', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC3 Fixture Mode', + 'options': list([ + 'standard', + 'battery', + ]), + }), + 'entity_id': 'select.nc3_fixture_mode', + 'state': 'standard', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc3_fixture_grid_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Grid action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_grid_action', + 'unique_id': '654321_relay_NC3_grid_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC3 Fixture Grid action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc3_fixture_grid_action', + 'state': 'not_powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc3_fixture_microgrid_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Microgrid action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_microgrid_action', + 'unique_id': '654321_relay_NC3_microgrid_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC3 Fixture Microgrid action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc3_fixture_microgrid_action', + 'state': 'powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'select', + 'entity_category': None, + 'entity_id': 'select.nc3_fixture_generator_action', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Generator action', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_generator_action', + 'unique_id': '654321_relay_NC3_generator_action', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC3 Fixture Generator action', + 'options': list([ + 'powered', + 'not_powered', + 'schedule', + 'none', + ]), + }), + 'entity_id': 'select.nc3_fixture_generator_action', + 'state': 'powered', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'switch', + 'entity_category': None, + 'entity_id': 'switch.nc3_fixture', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'relay_status', + 'unique_id': '654321_relay_NC3_relay_status', + 'unit_of_measurement': None, + }), + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'NC3 Fixture', + }), + 'entity_id': 'switch.nc3_fixture', + 'state': 'off', + }), + }), + ]), + }), + dict({ + 'device': dict({ + 'area_id': None, + 'config_entries': list([ + '45a36e55aaddb2007c5f6602e0c38e72', + ]), + 'config_entries_subentries': dict({ + '45a36e55aaddb2007c5f6602e0c38e72': list([ + None, + ]), + }), + 'configuration_url': None, + 'connections': list([ + ]), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'identifiers': list([ + list([ + 'enphase_envoy', + '1', + ]), + ]), + 'labels': list([ + ]), + 'manufacturer': 'Enphase', + 'model': 'Inverter', + 'model_id': None, + 'name': 'Inverter 1', + 'name_by_user': None, + 'primary_config_entry': '45a36e55aaddb2007c5f6602e0c38e72', + 'serial_number': '1', + 'sw_version': None, + }), + 'entities': list([ + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': None, + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor': dict({ + 'suggested_display_precision': 0, + }), + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': None, + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1', + 'unit_of_measurement': 'W', + }), + 'state': dict({ + 'attributes': dict({ + 'device_class': 'power', + 'friendly_name': 'Inverter 1', + 'state_class': 'measurement', + 'unit_of_measurement': 'W', + }), + 'entity_id': 'sensor.inverter_1', + 'state': '1', + }), + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_dc_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'DC voltage', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_voltage', + 'unique_id': '1_dc_voltage', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_dc_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'DC current', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'dc_current', + 'unique_id': '1_dc_current', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_ac_voltage', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'voltage', + 'original_icon': None, + 'original_name': 'AC voltage', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ac_voltage', + 'unique_id': '1_ac_voltage', + 'unit_of_measurement': 'V', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_ac_current', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'current', + 'original_icon': None, + 'original_name': 'AC current', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ac_current', + 'unique_id': '1_ac_current', + 'unit_of_measurement': 'A', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_frequency', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'frequency', + 'original_icon': None, + 'original_name': 'Frequency', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1_ac_frequency', + 'unit_of_measurement': 'Hz', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.inverter_1_temperature', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'temperature', + 'original_icon': None, + 'original_name': 'Temperature', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '1_temperature', + 'unit_of_measurement': '°C', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_lifetime_energy_production', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + 'sensor.private': dict({ + 'suggested_unit_of_measurement': 'kWh', + }), + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Lifetime energy production', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lifetime_energy', + 'unique_id': '1_lifetime_energy', + 'unit_of_measurement': 'kWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total_increasing', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_energy_production_today', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production today', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_today', + 'unique_id': '1_energy_today', + 'unit_of_measurement': 'Wh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.inverter_1_last_report_duration', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'duration', + 'original_icon': None, + 'original_name': 'Last report duration', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_report_duration', + 'unique_id': '1_last_report_duration', + 'unit_of_measurement': 's', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'total', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_energy_production_since_previous_report', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'energy', + 'original_icon': None, + 'original_name': 'Energy production since previous report', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'energy_produced', + 'unique_id': '1_energy_produced', + 'unit_of_measurement': 'mWh', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': dict({ + 'state_class': 'measurement', + }), + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': 'diagnostic', + 'entity_id': 'sensor.inverter_1_lifetime_maximum_power', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'power', + 'original_icon': None, + 'original_name': 'Lifetime maximum power', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'max_reported', + 'unique_id': '1_max_reported', + 'unit_of_measurement': 'W', + }), + 'state': None, + }), + dict({ + 'entity': dict({ + 'aliases': list([ + ]), + 'area_id': None, + 'capabilities': None, + 'categories': dict({ + }), + 'config_entry_id': '45a36e55aaddb2007c5f6602e0c38e72', + 'config_subentry_id': None, + 'device_class': None, + 'disabled_by': 'integration', + 'domain': 'sensor', + 'entity_category': None, + 'entity_id': 'sensor.inverter_1_last_reported', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'labels': list([ + ]), + 'name': None, + 'options': dict({ + }), + 'original_device_class': 'timestamp', + 'original_icon': None, + 'original_name': 'Last reported', + 'platform': 'enphase_envoy', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'last_reported', + 'unique_id': '1_last_reported', + 'unit_of_measurement': None, + }), + 'state': None, + }), + ]), + }), + ]), + 'envoy_model_data': dict({ + 'ctmeter_consumption': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000020', timestamp=1708006120, energy_delivered=21234, energy_received=22345, active_power=101, power_factor=0.21, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + 'ctmeter_consumption_phases': dict({ + 'L1': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000021', timestamp=1708006121, energy_delivered=212341, energy_received=223451, active_power=21, power_factor=0.22, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + 'L2': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000022', timestamp=1708006122, energy_delivered=212342, energy_received=223452, active_power=31, power_factor=0.23, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + 'L3': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000023', timestamp=1708006123, energy_delivered=212343, energy_received=223453, active_power=51, power_factor=0.24, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + }), + 'ctmeter_production': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000010', timestamp=1708006110, energy_delivered=11234, energy_received=12345, active_power=100, power_factor=0.11, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=['production-imbalance', 'power-on-unused-phase'])", + }), + 'ctmeter_production_phases': dict({ + 'L1': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000011', timestamp=1708006111, energy_delivered=112341, energy_received=123451, active_power=20, power_factor=0.12, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=['production-imbalance'])", + }), + 'L2': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000012', timestamp=1708006112, energy_delivered=112342, energy_received=123452, active_power=30, power_factor=0.13, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=['power-on-unused-phase'])", + }), + 'L3': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000013', timestamp=1708006113, energy_delivered=112343, energy_received=123453, active_power=50, power_factor=0.14, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=[])", + }), + }), + 'ctmeter_storage': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000030', timestamp=1708006120, energy_delivered=31234, energy_received=32345, active_power=103, power_factor=0.23, voltage=113, current=0.4, frequency=50.3, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + 'ctmeter_storage_phases': dict({ + 'L1': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000031', timestamp=1708006121, energy_delivered=312341, energy_received=323451, active_power=22, power_factor=0.32, voltage=113, current=0.4, frequency=50.3, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + 'L2': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000032', timestamp=1708006122, energy_delivered=312342, energy_received=323452, active_power=33, power_factor=0.23, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + 'L3': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000033', timestamp=1708006123, energy_delivered=312343, energy_received=323453, active_power=53, power_factor=0.24, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + }), + 'ctmeters': dict({ + 'net-consumption': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000020', timestamp=1708006120, energy_delivered=21234, energy_received=22345, active_power=101, power_factor=0.21, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + 'production': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000010', timestamp=1708006110, energy_delivered=11234, energy_received=12345, active_power=100, power_factor=0.11, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=['production-imbalance', 'power-on-unused-phase'])", + }), + 'storage': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000030', timestamp=1708006120, energy_delivered=31234, energy_received=32345, active_power=103, power_factor=0.23, voltage=113, current=0.4, frequency=50.3, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + }), + 'ctmeters_phases': dict({ + 'net-consumption': dict({ + 'L1': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000021', timestamp=1708006121, energy_delivered=212341, energy_received=223451, active_power=21, power_factor=0.22, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + 'L2': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000022', timestamp=1708006122, energy_delivered=212342, energy_received=223452, active_power=31, power_factor=0.23, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + 'L3': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000023', timestamp=1708006123, energy_delivered=212343, energy_received=223453, active_power=51, power_factor=0.24, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='net-consumption', metering_status='normal', status_flags=[])", + }), + }), + 'production': dict({ + 'L1': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000011', timestamp=1708006111, energy_delivered=112341, energy_received=123451, active_power=20, power_factor=0.12, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=['production-imbalance'])", + }), + 'L2': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000012', timestamp=1708006112, energy_delivered=112342, energy_received=123452, active_power=30, power_factor=0.13, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=['power-on-unused-phase'])", + }), + 'L3': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000013', timestamp=1708006113, energy_delivered=112343, energy_received=123453, active_power=50, power_factor=0.14, voltage=111, current=0.2, frequency=50.1, state='enabled', measurement_type='production', metering_status='normal', status_flags=[])", + }), + }), + 'storage': dict({ + 'L1': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000031', timestamp=1708006121, energy_delivered=312341, energy_received=323451, active_power=22, power_factor=0.32, voltage=113, current=0.4, frequency=50.3, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + 'L2': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000032', timestamp=1708006122, energy_delivered=312342, energy_received=323452, active_power=33, power_factor=0.23, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + 'L3': dict({ + '__type': "", + 'repr': "EnvoyMeterData(eid='100000033', timestamp=1708006123, energy_delivered=312343, energy_received=323453, active_power=53, power_factor=0.24, voltage=112, current=0.3, frequency=50.2, state='enabled', measurement_type='storage', metering_status='normal', status_flags=[])", + }), + }), + }), + 'dry_contact_settings': dict({ + 'NC1': dict({ + '__type': "", + 'repr': "EnvoyDryContactSettings(id='NC1', black_start=5.0, essential_end_time=32400.0, essential_start_time=57600.0, generator_action='shed', grid_action='shed', load_name='NC1 Fixture', manual_override=True, micro_grid_action='shed', mode='manual', override=True, priority=1.0, pv_serial_nb=[], soc_high=70.0, soc_low=25.0, type='LOAD')", + }), + 'NC2': dict({ + '__type': "", + 'repr': "EnvoyDryContactSettings(id='NC2', black_start=5.0, essential_end_time=57600.0, essential_start_time=32400.0, generator_action='shed', grid_action='apply', load_name='NC2 Fixture', manual_override=True, micro_grid_action='shed', mode='manual', override=True, priority=2.0, pv_serial_nb=[], soc_high=70.0, soc_low=30.0, type='LOAD')", + }), + 'NC3': dict({ + '__type': "", + 'repr': "EnvoyDryContactSettings(id='NC3', black_start=5.0, essential_end_time=57600.0, essential_start_time=32400.0, generator_action='apply', grid_action='shed', load_name='NC3 Fixture', manual_override=True, micro_grid_action='apply', mode='manual', override=True, priority=3.0, pv_serial_nb=[], soc_high=70.0, soc_low=30.0, type='NONE')", + }), + }), + 'dry_contact_status': dict({ + 'NC1': dict({ + '__type': "", + 'repr': "EnvoyDryContactStatus(id='NC1', status='open')", + }), + 'NC2': dict({ + '__type': "", + 'repr': "EnvoyDryContactStatus(id='NC2', status='closed')", + }), + 'NC3': dict({ + '__type': "", + 'repr': "EnvoyDryContactStatus(id='NC3', status='open')", + }), + }), + 'encharge_aggregate': dict({ + '__type': "", + 'repr': 'EnvoyEnchargeAggregate(available_energy=525, backup_reserve=526, state_of_charge=15, reserve_state_of_charge=15, configured_reserve_state_of_charge=15, max_available_capacity=3500)', + }), + 'encharge_inventory': dict({ + '123456': dict({ + '__type': "", + 'repr': "EnvoyEncharge(admin_state=6, admin_state_str='ENCHG_STATE_READY', bmu_firmware_version='2.1.34', comm_level_2_4_ghz=4, comm_level_sub_ghz=4, communicating=True, dc_switch_off=False, encharge_capacity=3500, encharge_revision=2, firmware_loaded_date=1695330323, firmware_version='2.6.5973_rel/22.11', installed_date=1695330323, last_report_date=1695769447, led_status=17, max_cell_temp=30, operating=True, part_number='830-01760-r37', percent_full=15, serial_number='123456', temperature=29, temperature_unit='C', zigbee_dongle_fw_version='100F')", + }), + }), + 'encharge_power': dict({ + '123456': dict({ + '__type': "", + 'repr': 'EnvoyEnchargePower(apparent_power_mva=0, real_power_mw=0, soc=15)', + }), + }), + 'enpower': dict({ + '__type': "", + 'repr': "EnvoyEnpower(grid_mode='multimode-ongrid', admin_state=24, admin_state_str='ENPWR_STATE_OPER_CLOSED', comm_level_2_4_ghz=5, comm_level_sub_ghz=5, communicating=True, firmware_loaded_date=1695330323, firmware_version='1.2.2064_release/20.34', installed_date=1695330323, last_report_date=1695769447, mains_admin_state='closed', mains_oper_state='closed', operating=True, part_number='830-01760-r37', serial_number='654321', temperature=79, temperature_unit='F', zigbee_dongle_fw_version='1009')", + }), + 'inverters': dict({ + '1': dict({ + '__type': "", + 'repr': "EnvoyInverter(serial_number='1', last_report_date=1, last_report_watts=1, max_report_watts=1, dc_voltage=None, dc_current=None, ac_voltage=None, ac_current=None, ac_frequency=None, temperature=None, lifetime_energy=None, energy_produced=None, energy_today=None, last_report_duration=None)", + }), + }), + 'system_consumption': dict({ + '__type': "", + 'repr': 'EnvoySystemConsumption(watt_hours_lifetime=1234, watt_hours_last_7_days=1234, watt_hours_today=1234, watts_now=1234)', + }), + 'system_consumption_phases': dict({ + 'L1': dict({ + '__type': "", + 'repr': 'EnvoySystemConsumption(watt_hours_lifetime=1322, watt_hours_last_7_days=1321, watt_hours_today=1323, watts_now=1324)', + }), + 'L2': dict({ + '__type': "", + 'repr': 'EnvoySystemConsumption(watt_hours_lifetime=2322, watt_hours_last_7_days=2321, watt_hours_today=2323, watts_now=2324)', + }), + 'L3': dict({ + '__type': "", + 'repr': 'EnvoySystemConsumption(watt_hours_lifetime=3322, watt_hours_last_7_days=3321, watt_hours_today=3323, watts_now=3324)', + }), + }), + 'system_production': dict({ + '__type': "", + 'repr': 'EnvoySystemProduction(watt_hours_lifetime=1234, watt_hours_last_7_days=1234, watt_hours_today=1234, watts_now=1234)', + }), + 'system_production_phases': dict({ + 'L1': dict({ + '__type': "", + 'repr': 'EnvoySystemProduction(watt_hours_lifetime=1232, watt_hours_last_7_days=1231, watt_hours_today=1233, watts_now=1234)', + }), + 'L2': dict({ + '__type': "", + 'repr': 'EnvoySystemProduction(watt_hours_lifetime=2232, watt_hours_last_7_days=2231, watt_hours_today=2233, watts_now=2234)', + }), + 'L3': dict({ + '__type': "", + 'repr': 'EnvoySystemProduction(watt_hours_lifetime=3232, watt_hours_last_7_days=3231, watt_hours_today=3233, watts_now=3234)', + }), + }), + 'tariff': dict({ + '__type': "", + 'repr': "EnvoyTariff(currency={'code': 'EUR'}, logger='mylogger', date='1695744220', storage_settings=EnvoyStorageSettings(mode='self-consumption', operation_mode_sub_type='', reserved_soc=15.0, very_low_soc=5, charge_from_grid=True, date='1695598084', opt_schedules=True), single_rate={'rate': 0.0, 'sell': 0.0}, seasons=[{'id': 'season_1', 'start': '1/1', 'days': [{'id': 'all_days', 'days': 'Mon,Tue,Wed,Thu,Fri,Sat,Sun', 'must_charge_start': 444, 'must_charge_duration': 35, 'must_charge_mode': 'CG', 'enable_discharge_to_grid': True, 'periods': [{'id': 'period_1', 'start': 480, 'rate': 0.1898}, {'id': 'filler', 'start': 1320, 'rate': 0.1034}]}], 'tiers': []}], seasons_sell=[])", + }), + }), + 'envoy_properties': dict({ + 'active interface': dict({ + }), + 'active_phasecount': 3, + 'ct_consumption_meter': 'net-consumption', + 'ct_count': 2, + 'ct_meters': list([ + 'production', + 'net-consumption', + 'storage', + ]), + 'ct_production_meter': 'production', + 'ct_storage_meter': 'storage', + 'envoy_firmware': '7.1.2', + 'envoy_model': 'Envoy, phases: 3, phase mode: split, net-consumption CT, production CT, storage CT', + 'part_number': '123456789', + 'phase_count': 3, + 'phase_mode': 'three', + 'supported_features': list([ + 'INVERTERS', + 'METERING', + 'NET_CONSUMPTION', + 'ENCHARGE', + 'ENPOWER', + 'PRODUCTION', + 'THREEPHASE', + 'CTMETERS', + ]), + }), + 'fixtures': dict({ + }), + 'raw_data': dict({ + 'varies_by': 'firmware_version', + }), + }) +# --- diff --git a/tests/components/enphase_envoy/test_diagnostics.py b/tests/components/enphase_envoy/test_diagnostics.py index 87e6842616d03..06a8ac58e11a4 100644 --- a/tests/components/enphase_envoy/test_diagnostics.py +++ b/tests/components/enphase_envoy/test_diagnostics.py @@ -94,6 +94,14 @@ async def test_entry_diagnostics_with_fixtures_with_error( ) == snapshot(exclude=limit_diagnostic_attrs) +@pytest.mark.parametrize( + ("mock_envoy"), + [ + "envoy_metered_batt_relay", + "envoy", + ], + indirect=["mock_envoy"], +) async def test_entry_diagnostics_with_interface_information( hass: HomeAssistant, hass_client: ClientSessionGenerator, diff --git a/tests/components/flux_led/test_init.py b/tests/components/flux_led/test_init.py index 8e3bb03dca206..663b46f65a7d7 100644 --- a/tests/components/flux_led/test_init.py +++ b/tests/components/flux_led/test_init.py @@ -238,8 +238,10 @@ async def test_time_sync_startup_and_next_day(hass: HomeAssistant) -> None: assert config_entry.state is ConfigEntryState.LOADED assert len(bulb.async_set_time.mock_calls) == 1 - async_fire_time_changed(hass, utcnow() + timedelta(hours=24)) - await hass.async_block_till_done() + + with _patch_discovery(), _patch_wifibulb(device=bulb): + async_fire_time_changed(hass, utcnow() + timedelta(hours=24)) + await hass.async_block_till_done() assert len(bulb.async_set_time.mock_calls) == 2 diff --git a/tests/components/goodwe/conftest.py b/tests/components/goodwe/conftest.py index 0b4ce67d12132..ba0ad1bfa56dc 100644 --- a/tests/components/goodwe/conftest.py +++ b/tests/components/goodwe/conftest.py @@ -1,16 +1,22 @@ """Fixtures for the Aladdin Connect integration tests.""" -from unittest.mock import AsyncMock, MagicMock +from collections.abc import Generator +from unittest.mock import AsyncMock, MagicMock, patch from goodwe import Inverter +from goodwe.const import GOODWE_UDP_PORT import pytest +TEST_HOST = "1.2.3.4" +TEST_PORT = GOODWE_UDP_PORT +TEST_SERIAL = "123456789" + @pytest.fixture(name="mock_inverter") -def fixture_mock_inverter(): +def fixture_mock_inverter() -> Generator[MagicMock]: """Set up inverter fixture.""" mock_inverter = MagicMock(spec=Inverter) - mock_inverter.serial_number = "dummy_serial_nr" + mock_inverter.serial_number = TEST_SERIAL mock_inverter.arm_version = 1 mock_inverter.arm_svn_version = 2 mock_inverter.arm_firmware = "dummy.arm.version" @@ -23,4 +29,11 @@ def fixture_mock_inverter(): mock_inverter.read_runtime_data = AsyncMock(return_value={}) - return mock_inverter + with ( + patch( + "homeassistant.components.goodwe.config_flow.connect", + return_value=mock_inverter, + ), + patch("homeassistant.components.goodwe.connect", return_value=mock_inverter), + ): + yield mock_inverter diff --git a/tests/components/goodwe/snapshots/test_diagnostics.ambr b/tests/components/goodwe/snapshots/test_diagnostics.ambr index 40ed22195d5f1..6d2a8a6fd6647 100644 --- a/tests/components/goodwe/snapshots/test_diagnostics.ambr +++ b/tests/components/goodwe/snapshots/test_diagnostics.ambr @@ -3,8 +3,9 @@ dict({ 'config_entry': dict({ 'data': dict({ - 'host': 'localhost', + 'host': '1.2.3.4', 'model_family': 'ET', + 'port': 8899, }), 'disabled_by': None, 'discovery_keys': dict({ @@ -21,7 +22,7 @@ ]), 'title': 'Mock Title', 'unique_id': None, - 'version': 1, + 'version': 2, }), 'inverter': dict({ 'arm_firmware': 'dummy.arm.version', diff --git a/tests/components/goodwe/test_config_flow.py b/tests/components/goodwe/test_config_flow.py index bede53ec9edc6..5ec01487ad328 100644 --- a/tests/components/goodwe/test_config_flow.py +++ b/tests/components/goodwe/test_config_flow.py @@ -1,8 +1,9 @@ """Test the Goodwe config flow.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import MagicMock, patch from goodwe import InverterError +from goodwe.const import GOODWE_UDP_PORT from homeassistant.components.goodwe.const import ( CONF_MODEL_FAMILY, @@ -10,24 +11,19 @@ DOMAIN, ) from homeassistant.config_entries import SOURCE_USER -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from .conftest import TEST_SERIAL + from tests.common import MockConfigEntry TEST_HOST = "1.2.3.4" -TEST_SERIAL = "123456789" - +TEST_PORT = GOODWE_UDP_PORT -def mock_inverter(): - """Get a mock object of the inverter.""" - goodwe_inverter = AsyncMock() - goodwe_inverter.serial_number = TEST_SERIAL - return goodwe_inverter - -async def test_manual_setup(hass: HomeAssistant) -> None: +async def test_manual_setup(hass: HomeAssistant, mock_inverter: MagicMock) -> None: """Test manually setting up.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} @@ -37,10 +33,6 @@ async def test_manual_setup(hass: HomeAssistant) -> None: assert not result["errors"] with ( - patch( - "homeassistant.components.goodwe.config_flow.connect", - return_value=mock_inverter(), - ), patch( "homeassistant.components.goodwe.async_setup_entry", return_value=True ) as mock_setup_entry, @@ -54,15 +46,20 @@ async def test_manual_setup(hass: HomeAssistant) -> None: assert result["title"] == DEFAULT_NAME assert result["data"] == { CONF_HOST: TEST_HOST, - CONF_MODEL_FAMILY: "AsyncMock", + CONF_PORT: TEST_PORT, + CONF_MODEL_FAMILY: "MagicMock", } assert len(mock_setup_entry.mock_calls) == 1 -async def test_manual_setup_already_exists(hass: HomeAssistant) -> None: +async def test_manual_setup_already_exists( + hass: HomeAssistant, mock_inverter: MagicMock +) -> None: """Test manually setting up and the device already exists.""" entry = MockConfigEntry( - domain=DOMAIN, data={CONF_HOST: TEST_HOST}, unique_id=TEST_SERIAL + domain=DOMAIN, + data={CONF_HOST: TEST_HOST}, + unique_id=TEST_SERIAL, ) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( @@ -72,13 +69,7 @@ async def test_manual_setup_already_exists(hass: HomeAssistant) -> None: assert result["step_id"] == "user" assert not result["errors"] - with ( - patch( - "homeassistant.components.goodwe.config_flow.connect", - return_value=mock_inverter(), - ), - patch("homeassistant.components.goodwe.async_setup_entry", return_value=True), - ): + with patch("homeassistant.components.goodwe.async_setup_entry", return_value=True): result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_HOST: TEST_HOST} ) diff --git a/tests/components/goodwe/test_diagnostics.py b/tests/components/goodwe/test_diagnostics.py index fa90889e75e7e..aa6a49890b437 100644 --- a/tests/components/goodwe/test_diagnostics.py +++ b/tests/components/goodwe/test_diagnostics.py @@ -1,15 +1,17 @@ -"""Test the CO2Signal diagnostics.""" +"""Test the GoodWe diagnostics.""" -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock from syrupy.assertion import SnapshotAssertion from syrupy.filters import props from homeassistant.components.goodwe import CONF_MODEL_FAMILY, DOMAIN -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component +from .conftest import TEST_HOST, TEST_PORT + from tests.common import MockConfigEntry from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.typing import ClientSessionGenerator @@ -24,13 +26,17 @@ async def test_entry_diagnostics( """Test config entry diagnostics.""" config_entry = MockConfigEntry( + version=2, domain=DOMAIN, - data={CONF_HOST: "localhost", CONF_MODEL_FAMILY: "ET"}, + data={ + CONF_HOST: TEST_HOST, + CONF_PORT: TEST_PORT, + CONF_MODEL_FAMILY: "ET", + }, entry_id="3bd2acb0e4f0476d40865546d0d91921", ) config_entry.add_to_hass(hass) - with patch("homeassistant.components.goodwe.connect", return_value=mock_inverter): - assert await async_setup_component(hass, DOMAIN, {}) + assert await async_setup_component(hass, DOMAIN, {}) result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) assert result == snapshot(exclude=props("created_at", "modified_at")) diff --git a/tests/components/goodwe/test_init.py b/tests/components/goodwe/test_init.py new file mode 100644 index 0000000000000..ee8060018aec7 --- /dev/null +++ b/tests/components/goodwe/test_init.py @@ -0,0 +1,36 @@ +"""Test the GoodWe initialization.""" + +from unittest.mock import MagicMock + +from homeassistant.components.goodwe import CONF_MODEL_FAMILY, DOMAIN +from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from .conftest import TEST_HOST, TEST_PORT + +from tests.common import MockConfigEntry + + +async def test_migration( + hass: HomeAssistant, + mock_inverter: MagicMock, +) -> None: + """Test config entry migration.""" + + config_entry = MockConfigEntry( + version=1, + domain=DOMAIN, + data={ + CONF_HOST: TEST_HOST, + CONF_MODEL_FAMILY: "MagicMock", + }, + entry_id="3bd2acb0e4f0476d40865546d0d91921", + ) + config_entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, {}) + + assert config_entry.version == 2 + assert config_entry.data[CONF_HOST] == TEST_HOST + assert config_entry.data[CONF_MODEL_FAMILY] == "MagicMock" + assert config_entry.data[CONF_PORT] == TEST_PORT diff --git a/tests/components/local_todo/snapshots/test_todo.ambr b/tests/components/local_todo/snapshots/test_todo.ambr index 15a44ff8c2700..a883fd6ce9def 100644 --- a/tests/components/local_todo/snapshots/test_todo.ambr +++ b/tests/components/local_todo/snapshots/test_todo.ambr @@ -2,6 +2,7 @@ # name: test_parse_existing_ics[completed] list([ dict({ + 'completed': '2023-10-25T01:40:11+00:00', 'status': 'completed', 'summary': 'Complete Task', 'uid': '077cb7f2-6c89-11ee-b2a9-0242ac110002', diff --git a/tests/components/local_todo/test_todo.py b/tests/components/local_todo/test_todo.py index 253adebd75776..af2cb839abf4d 100644 --- a/tests/components/local_todo/test_todo.py +++ b/tests/components/local_todo/test_todo.py @@ -1,9 +1,11 @@ """Tests for todo platform of local_todo.""" from collections.abc import Awaitable, Callable +from datetime import datetime import textwrap from typing import Any +from freezegun import freeze_time import pytest from syrupy.assertion import SnapshotAssertion @@ -19,6 +21,7 @@ ) from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant +from homeassistant.util import dt as dt_util from .conftest import TEST_ENTITY @@ -239,7 +242,11 @@ async def test_bulk_remove( [ ( {ATTR_STATUS: "completed"}, - {**EXPECTED_UPDATE_ITEM, "status": "completed"}, + { + **EXPECTED_UPDATE_ITEM, + "status": "completed", + "completed": "2023-11-18T08:00:00+00:00", + }, "0", ), ( @@ -291,13 +298,15 @@ async def test_update_item( assert state.state == "1" # Update item - await hass.services.async_call( - TODO_DOMAIN, - TodoServices.UPDATE_ITEM, - {ATTR_ITEM: item["uid"], **item_data}, - target={ATTR_ENTITY_ID: TEST_ENTITY}, - blocking=True, - ) + update_time = datetime(2023, 11, 18, 8, 0, 0, tzinfo=dt_util.UTC) + with freeze_time(update_time): + await hass.services.async_call( + TODO_DOMAIN, + TodoServices.UPDATE_ITEM, + {ATTR_ITEM: item["uid"], **item_data}, + target={ATTR_ENTITY_ID: TEST_ENTITY}, + blocking=True, + ) # Verify item is updated items = await ws_get_items() @@ -323,6 +332,7 @@ async def test_update_item( "status": "completed", "description": "Additional detail", "due": "2024-01-01", + "completed": "2023-11-18T08:00:00+00:00", }, ), ( @@ -414,13 +424,15 @@ async def test_update_existing_field( assert item["status"] == "needs_action" # Perform update - await hass.services.async_call( - TODO_DOMAIN, - TodoServices.UPDATE_ITEM, - {ATTR_ITEM: item["uid"], **item_data}, - target={ATTR_ENTITY_ID: TEST_ENTITY}, - blocking=True, - ) + update_time = datetime(2023, 11, 18, 8, 0, 0, tzinfo=dt_util.UTC) + with freeze_time(update_time): + await hass.services.async_call( + TODO_DOMAIN, + TodoServices.UPDATE_ITEM, + {ATTR_ITEM: item["uid"], **item_data}, + target={ATTR_ENTITY_ID: TEST_ENTITY}, + blocking=True, + ) # Verify item is updated items = await ws_get_items() @@ -618,6 +630,7 @@ async def test_move_item_previous_unknown( UID:077cb7f2-6c89-11ee-b2a9-0242ac110002 CREATED:20231017T010348 LAST-MODIFIED:20231024T014011 + COMPLETED:20231025T014011Z SEQUENCE:1 STATUS:COMPLETED SUMMARY:Complete Task diff --git a/tests/components/reolink/conftest.py b/tests/components/reolink/conftest.py index 82d96d32622a6..a766846175a75 100644 --- a/tests/components/reolink/conftest.py +++ b/tests/components/reolink/conftest.py @@ -106,6 +106,7 @@ def _init_host_mock(host_mock: MagicMock) -> None: host_mock.protocol = "rtsp" host_mock.channels = [0] host_mock.stream_channels = [0] + host_mock.num_cameras = 1 host_mock.new_devices = False host_mock.sw_version_update_required = False host_mock.hardware_version = "IPC_00000" diff --git a/tests/components/reolink/test_binary_sensor.py b/tests/components/reolink/test_binary_sensor.py index b0c387e2cd329..34cebe60a40f0 100644 --- a/tests/components/reolink/test_binary_sensor.py +++ b/tests/components/reolink/test_binary_sensor.py @@ -5,7 +5,7 @@ from freezegun.api import FrozenDateTimeFactory -from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL +from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import STATE_OFF, STATE_ON, Platform from homeassistant.core import HomeAssistant @@ -35,7 +35,7 @@ async def test_motion_sensor( assert hass.states.get(entity_id).state == STATE_ON reolink_host.motion_detected.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -69,7 +69,7 @@ async def test_smart_ai_sensor( assert hass.states.get(entity_id).state == STATE_ON reolink_host.baichuan.smart_ai_state.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -94,7 +94,7 @@ async def test_index_sensor( assert hass.states.get(entity_id).state == STATE_ON reolink_host.baichuan.io_input_state.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() diff --git a/tests/components/reolink/test_config_flow.py b/tests/components/reolink/test_config_flow.py index 0a837a97b20af..e2e449e82f9e2 100644 --- a/tests/components/reolink/test_config_flow.py +++ b/tests/components/reolink/test_config_flow.py @@ -16,7 +16,7 @@ ) from homeassistant import config_entries -from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL +from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN from homeassistant.components.reolink.config_flow import DEFAULT_PROTOCOL from homeassistant.components.reolink.const import ( CONF_BC_ONLY, @@ -564,7 +564,7 @@ async def test_dhcp_ip_update_aborted_if_wrong_mac( # ensure the last_update_succes is False for the device_coordinator. reolink_host.get_states.side_effect = ReolinkError("Test error") - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -661,7 +661,7 @@ async def test_dhcp_ip_update( # ensure the last_update_succes is False for the device_coordinator. reolink_host.get_states.side_effect = ReolinkError("Test error") - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() diff --git a/tests/components/reolink/test_host.py b/tests/components/reolink/test_host.py index 194d038a32a12..fb2c1d845b6b3 100644 --- a/tests/components/reolink/test_host.py +++ b/tests/components/reolink/test_host.py @@ -10,7 +10,7 @@ from reolink_aio.enums import SubType from reolink_aio.exceptions import NotSupportedError, ReolinkError, SubscriptionError -from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL +from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN from homeassistant.components.reolink.host import ( FIRST_ONVIF_LONG_POLL_TIMEOUT, FIRST_ONVIF_TIMEOUT, @@ -258,7 +258,7 @@ async def test_renew( await hass.async_block_till_done() assert config_entry.state is ConfigEntryState.LOADED - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -266,7 +266,7 @@ async def test_renew( reolink_host.renew.side_effect = SubscriptionError("Test error") - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -275,7 +275,7 @@ async def test_renew( reolink_host.subscribe.reset_mock() reolink_host.subscribe.side_effect = SubscriptionError("Test error") - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -341,7 +341,7 @@ async def test_long_poll_stop_when_push( webhook_id = config_entry.runtime_data.host.webhook_id await client.post(f"/api/webhook/{webhook_id}") - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() diff --git a/tests/components/reolink/test_init.py b/tests/components/reolink/test_init.py index 1fca265ece764..0e712542f8d96 100644 --- a/tests/components/reolink/test_init.py +++ b/tests/components/reolink/test_init.py @@ -14,7 +14,7 @@ ) from homeassistant.components.reolink import ( - DEVICE_UPDATE_INTERVAL, + DEVICE_UPDATE_INTERVAL_MIN, FIRMWARE_UPDATE_INTERVAL, NUM_CRED_ERRORS, ) @@ -174,7 +174,7 @@ async def test_credential_error_three( issue_id = f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}" for _ in range(NUM_CRED_ERRORS): assert (HOMEASSISTANT_DOMAIN, issue_id) not in issue_registry.issues - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -917,7 +917,7 @@ async def test_new_device_discovered( assert reolink_host.logout.call_count == 0 reolink_host.new_devices = True - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -988,7 +988,7 @@ async def test_LoginPrivacyModeError( reolink_host.baichuan.check_subscribe_events.reset_mock() assert reolink_host.baichuan.check_subscribe_events.call_count == 0 - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() diff --git a/tests/components/reolink/test_select.py b/tests/components/reolink/test_select.py index e74bcf8fc753a..7ef4baf4d6100 100644 --- a/tests/components/reolink/test_select.py +++ b/tests/components/reolink/test_select.py @@ -7,7 +7,7 @@ from reolink_aio.api import Chime from reolink_aio.exceptions import InvalidParameterError, ReolinkError -from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL +from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN from homeassistant.components.select import DOMAIN as SELECT_DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( @@ -68,7 +68,7 @@ async def test_floodlight_mode_select( ) reolink_host.whiteled_mode.return_value = -99 # invalid value - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -142,7 +142,7 @@ async def test_host_scene_select( ) reolink_host.baichuan.active_scene = "Invalid value" - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -202,7 +202,7 @@ async def test_chime_select( # Test unavailable reolink_chime.event_info = {} reolink_chime.update_enums() - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() diff --git a/tests/components/reolink/test_switch.py b/tests/components/reolink/test_switch.py index a8354b32b2260..dad0f7135efd9 100644 --- a/tests/components/reolink/test_switch.py +++ b/tests/components/reolink/test_switch.py @@ -7,7 +7,7 @@ from reolink_aio.api import Chime from reolink_aio.exceptions import ReolinkError -from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL +from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL_MIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( @@ -45,7 +45,7 @@ async def test_switch( assert hass.states.get(entity_id).state == STATE_ON reolink_host.audio_record.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -91,7 +91,7 @@ async def test_switch( reolink_host.set_audio.reset_mock(side_effect=True) reolink_host.camera_online.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -118,7 +118,7 @@ async def test_host_switch( assert hass.states.get(entity_id).state == STATE_ON reolink_host.email_enabled.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -183,7 +183,7 @@ async def test_chime_switch( assert hass.states.get(entity_id).state == STATE_ON reolink_chime.led_state = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() @@ -248,7 +248,7 @@ async def test_rule_switch( assert hass.states.get(entity_id).state == STATE_ON reolink_host.baichuan.rule_enabled.return_value = False - freezer.tick(DEVICE_UPDATE_INTERVAL) + freezer.tick(DEVICE_UPDATE_INTERVAL_MIN) async_fire_time_changed(hass) await hass.async_block_till_done() diff --git a/tests/components/ruuvitag_ble/__init__.py b/tests/components/ruuvitag_ble/__init__.py index e39900689ccb8..5fa0a6df472fb 100644 --- a/tests/components/ruuvitag_ble/__init__.py +++ b/tests/components/ruuvitag_ble/__init__.py @@ -1 +1 @@ -"""Test package for RuuviTag BLE sensor integration.""" +"""Test package for Ruuvi BLE sensor integration.""" diff --git a/tests/components/ruuvitag_ble/fixtures.py b/tests/components/ruuvitag_ble/fixtures.py index ff8e3b5dc8a8e..f9c7834f62359 100644 --- a/tests/components/ruuvitag_ble/fixtures.py +++ b/tests/components/ruuvitag_ble/fixtures.py @@ -1,4 +1,4 @@ -"""Fixtures for testing RuuviTag BLE.""" +"""Fixtures for testing Ruuvi BLE.""" from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo diff --git a/tests/components/ruuvitag_ble/test_sensor.py b/tests/components/ruuvitag_ble/test_sensor.py index 482a6f852db6c..a1dd9af1f2ce4 100644 --- a/tests/components/ruuvitag_ble/test_sensor.py +++ b/tests/components/ruuvitag_ble/test_sensor.py @@ -1,4 +1,4 @@ -"""Test the Ruuvitag BLE sensors.""" +"""Test the Ruuvi BLE sensors.""" from __future__ import annotations @@ -35,7 +35,7 @@ async def test_sensors( snapshot: SnapshotAssertion, service_info: BluetoothServiceInfo, ) -> None: - """Test the RuuviTag BLE sensors.""" + """Test the Ruuvi BLE sensors.""" entry = MockConfigEntry(domain=DOMAIN, unique_id=service_info.address) entry.add_to_hass(hass) diff --git a/tests/components/senz/conftest.py b/tests/components/senz/conftest.py index 60a1bfd0c4777..df1ab5d9c4db9 100644 --- a/tests/components/senz/conftest.py +++ b/tests/components/senz/conftest.py @@ -14,9 +14,10 @@ ) from homeassistant.components.senz.const import DOMAIN from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.setup import async_setup_component -from .const import CLIENT_ID, CLIENT_SECRET +from .const import CLIENT_ID, CLIENT_SECRET, ENTRY_UNIQUE_ID from tests.common import ( MockConfigEntry, @@ -63,7 +64,7 @@ def mock_expires_at() -> float: def mock_config_entry(hass: HomeAssistant, expires_at: float) -> MockConfigEntry: """Return the default mocked config entry.""" config_entry = MockConfigEntry( - minor_version=1, + minor_version=2, domain=DOMAIN, title="Senz test", data={ @@ -77,6 +78,7 @@ def mock_config_entry(hass: HomeAssistant, expires_at: float) -> MockConfigEntry }, }, entry_id="senz_test", + unique_id=ENTRY_UNIQUE_ID, ) config_entry.add_to_hass(hass) return config_entry @@ -109,3 +111,26 @@ async def setup_credentials(hass: HomeAssistant) -> None: ), DOMAIN, ) + + +@pytest.fixture +def unique_id() -> str: + """Return a unique ID.""" + return ENTRY_UNIQUE_ID + + +@pytest.fixture +async def access_token(hass: HomeAssistant, unique_id: str) -> str: + """Return a valid access token.""" + return config_entry_oauth2_flow._encode_jwt( + hass, + { + "sub": unique_id, + "aud": [], + "scp": [ + "rest_api", + "offline_access", + ], + "ou_code": "NA", + }, + ) diff --git a/tests/components/senz/const.py b/tests/components/senz/const.py index 19dfac6238b35..20c1e116529d4 100644 --- a/tests/components/senz/const.py +++ b/tests/components/senz/const.py @@ -2,3 +2,5 @@ CLIENT_ID = "test_client_id" CLIENT_SECRET = "test_client_secret" + +ENTRY_UNIQUE_ID = "test_unique_id" diff --git a/tests/components/senz/test_config_flow.py b/tests/components/senz/test_config_flow.py index b9e28115c46b7..1892abd454bf0 100644 --- a/tests/components/senz/test_config_flow.py +++ b/tests/components/senz/test_config_flow.py @@ -12,20 +12,26 @@ ) from homeassistant.components.senz.const import DOMAIN from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.setup import async_setup_component -from .const import CLIENT_ID, CLIENT_SECRET +from .const import CLIENT_ID, CLIENT_SECRET, ENTRY_UNIQUE_ID +from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMocker from tests.typing import ClientSessionGenerator +REDIRECT_PATH = "/auth/external/callback" +REDIRECT_URL = "https://example.com" + REDIRECT_PATH + @pytest.mark.usefixtures("current_request_with_host") async def test_full_flow( hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator, aioclient_mock: AiohttpClientMocker, + access_token: str, ) -> None: """Check full flow.""" await async_setup_component(hass, DOMAIN, {}) @@ -42,18 +48,18 @@ async def test_full_flow( hass, { "flow_id": result["flow_id"], - "redirect_uri": "https://example.com/auth/external/callback", + "redirect_uri": REDIRECT_URL, }, ) assert result["url"] == ( f"{AUTHORIZATION_ENDPOINT}?response_type=code&client_id={CLIENT_ID}" - "&redirect_uri=https://example.com/auth/external/callback" + f"&redirect_uri={REDIRECT_URL}" f"&state={state}&scope=restapi+offline_access" ) client = await hass_client_no_auth() - resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + resp = await client.get(f"{REDIRECT_PATH}?code=abcd&state={state}") assert resp.status == 200 assert resp.headers["content-type"] == "text/html; charset=utf-8" @@ -61,7 +67,7 @@ async def test_full_flow( TOKEN_ENDPOINT, json={ "refresh_token": "mock-refresh-token", - "access_token": "mock-access-token", + "access_token": access_token, "type": "Bearer", "expires_in": 60, }, @@ -74,3 +80,202 @@ async def test_full_flow( assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(mock_setup.mock_calls) == 1 + + +@pytest.mark.usefixtures("current_request_with_host") +async def test_duplicate_flow( + hass: HomeAssistant, + hass_client_no_auth: ClientSessionGenerator, + aioclient_mock: AiohttpClientMocker, + mock_config_entry: MockConfigEntry, + access_token: str, +) -> None: + """Check full flow with duplicate entry.""" + mock_config_entry.add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URL, + }, + ) + + assert result["url"] == ( + f"{AUTHORIZATION_ENDPOINT}?response_type=code&client_id={CLIENT_ID}" + f"&redirect_uri={REDIRECT_URL}" + f"&state={state}&scope=restapi+offline_access" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"{REDIRECT_PATH}?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + TOKEN_ENDPOINT, + json={ + "refresh_token": "mock-refresh-token", + "access_token": access_token, + "type": "Bearer", + "expires_in": 60, + }, + ) + + with patch("homeassistant.components.senz.async_setup_entry", return_value=True): + result2 = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert result2["type"] is FlowResultType.ABORT + assert result2["reason"] == "already_configured" + + +@pytest.mark.usefixtures("current_request_with_host") +async def test_reauth_flow( + hass: HomeAssistant, + hass_client_no_auth: ClientSessionGenerator, + aioclient_mock: AiohttpClientMocker, + mock_config_entry: MockConfigEntry, + access_token: str, + expires_at: float, +) -> None: + """Test reauth step with correct params.""" + + CURRENT_TOKEN = { + "auth_implementation": DOMAIN, + "token": { + "access_token": access_token, + "expires_in": 86399, + "refresh_token": "3012bc9f-7a65-4240-b817-9154ffdcc30f", + "token_type": "Bearer", + "expires_at": expires_at, + }, + } + assert hass.config_entries.async_update_entry( + mock_config_entry, + data=CURRENT_TOKEN, + ) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + result = await mock_config_entry.start_reauth_flow(hass) + + assert result["step_id"] == "reauth_confirm" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + assert result["step_id"] == "auth" + + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URL, + }, + ) + assert result["url"] == ( + f"{AUTHORIZATION_ENDPOINT}?response_type=code&client_id={CLIENT_ID}" + f"&redirect_uri={REDIRECT_URL}" + f"&state={state}&scope=restapi+offline_access" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"{REDIRECT_PATH}?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + TOKEN_ENDPOINT, + json={ + "refresh_token": "updated-refresh-token", + "access_token": access_token, + "type": "Bearer", + "expires_in": "60", + }, + ) + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert result.get("type") is FlowResultType.ABORT + assert result.get("reason") == "reauth_successful" + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + +@pytest.mark.usefixtures("current_request_with_host") +@pytest.mark.parametrize( + ("unique_id", "expected_result"), + [ + (ENTRY_UNIQUE_ID, "reconfigure_successful"), + ("different_unique_id", "account_mismatch"), + ], +) +async def test_reconfiguration_flow( + hass: HomeAssistant, + hass_client_no_auth: ClientSessionGenerator, + aioclient_mock: AiohttpClientMocker, + mock_config_entry: MockConfigEntry, + access_token: str, + unique_id: str, + expected_result: str, + expires_at: float, +) -> None: + """Test reconfigure step with correct params.""" + + CURRENT_TOKEN = { + "auth_implementation": DOMAIN, + "token": { + "access_token": access_token, + "expires_in": 86399, + "refresh_token": "3012bc9f-7a65-4240-b817-9154ffdcc30f", + "token_type": "Bearer", + "expires_at": expires_at, + }, + } + assert hass.config_entries.async_update_entry( + mock_config_entry, + data=CURRENT_TOKEN, + ) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + result = await mock_config_entry.start_reconfigure_flow(hass) + + assert result["step_id"] == "auth" + + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URL, + }, + ) + assert result["url"] == ( + f"{AUTHORIZATION_ENDPOINT}?response_type=code&client_id={CLIENT_ID}" + f"&redirect_uri={REDIRECT_URL}" + f"&state={state}&scope=restapi+offline_access" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"{REDIRECT_PATH}?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + TOKEN_ENDPOINT, + json={ + "refresh_token": "updated-refresh-token", + "access_token": access_token, + "type": "Bearer", + "expires_in": "60", + }, + ) + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + await hass.async_block_till_done() + + assert result.get("type") is FlowResultType.ABORT + assert result.get("reason") == expected_result + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 diff --git a/tests/components/senz/test_init.py b/tests/components/senz/test_init.py index 9b3f46deafd52..1dad116d8347d 100644 --- a/tests/components/senz/test_init.py +++ b/tests/components/senz/test_init.py @@ -1,7 +1,14 @@ """Test init of senz integration.""" +from http import HTTPStatus +import time from unittest.mock import MagicMock, patch +from aiosenz import TOKEN_ENDPOINT +from httpx import HTTPStatusError, RequestError +import pytest + +from homeassistant.components.senz.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.helpers.config_entry_oauth2_flow import ( @@ -9,8 +16,10 @@ ) from . import setup_integration +from .const import ENTRY_UNIQUE_ID from tests.common import MockConfigEntry +from tests.test_util.aiohttp import AiohttpClientMocker async def test_load_unload_entry( @@ -43,3 +52,110 @@ async def test_oauth_implementation_not_available( await hass.async_block_till_done() assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_migrate_config_entry( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_senz_client: MagicMock, + expires_at: float, + access_token: str, +) -> None: + """Test migration of config entry.""" + mock_entry_v1_1 = MockConfigEntry( + version=1, + minor_version=1, + domain=DOMAIN, + title="SENZ test", + data={ + "auth_implementation": DOMAIN, + "token": { + "access_token": access_token, + "scope": "rest_api offline_access", + "expires_in": 86399, + "refresh_token": "3012bc9f-7a65-4240-b817-9154ffdcc30f", + "token_type": "Bearer", + "expires_at": expires_at, + }, + }, + entry_id="senz_test", + ) + + await setup_integration(hass, mock_entry_v1_1) + assert mock_entry_v1_1.version == 1 + assert mock_entry_v1_1.minor_version == 2 + assert mock_entry_v1_1.unique_id == ENTRY_UNIQUE_ID + + +@pytest.mark.parametrize( + ("expires_at", "status", "expected_state"), + [ + ( + time.time() - 3600, + HTTPStatus.UNAUTHORIZED, + ConfigEntryState.SETUP_ERROR, + ), + ( + time.time() - 3600, + HTTPStatus.INTERNAL_SERVER_ERROR, + ConfigEntryState.SETUP_ERROR, + ), + ], + ids=["unauthorized", "internal_server_error"], +) +async def test_expired_token_refresh_failure( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + aioclient_mock: AiohttpClientMocker, + status: HTTPStatus, + expected_state: ConfigEntryState, +) -> None: + """Test failure while refreshing token with a transient error.""" + + aioclient_mock.clear_requests() + aioclient_mock.post( + TOKEN_ENDPOINT, + status=status, + ) + + await setup_integration(hass, mock_config_entry) + + assert mock_config_entry.state is expected_state + + +@pytest.mark.parametrize( + ("error", "expected_state"), + [ + ( + HTTPStatusError( + message="Exception", + request=None, + response=MagicMock(status_code=HTTPStatus.UNAUTHORIZED), + ), + ConfigEntryState.SETUP_ERROR, + ), + ( + HTTPStatusError( + message="Exception", + request=None, + response=MagicMock(status_code=HTTPStatus.FORBIDDEN), + ), + ConfigEntryState.SETUP_RETRY, + ), + (RequestError("Exception"), ConfigEntryState.SETUP_RETRY), + ], + ids=["unauthorized", "forbidden", "request_error"], +) +async def test_setup_errors( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_senz_client: MagicMock, + error: Exception, + expected_state: ConfigEntryState, +) -> None: + """Test setup failure due to unauthorized error.""" + mock_senz_client.get_account.side_effect = error + + await setup_integration(hass, mock_config_entry) + + assert mock_config_entry.state is expected_state diff --git a/tests/components/todo/test_init.py b/tests/components/todo/test_init.py index adada97a9e4ed..4b1fdcf1d9265 100644 --- a/tests/components/todo/test_init.py +++ b/tests/components/todo/test_init.py @@ -1086,6 +1086,7 @@ async def test_subscribe( "status": "needs_action", "due": None, "description": None, + "completed": None, }, { "summary": "Item #2", @@ -1093,6 +1094,7 @@ async def test_subscribe( "status": "completed", "due": None, "description": None, + "completed": None, }, ] } @@ -1112,6 +1114,7 @@ async def test_subscribe( "status": "needs_action", "due": None, "description": None, + "completed": None, }, { "summary": "Item #2", @@ -1119,6 +1122,7 @@ async def test_subscribe( "status": "completed", "due": None, "description": None, + "completed": None, }, { "summary": "Item #3", @@ -1126,6 +1130,7 @@ async def test_subscribe( "status": "needs_action", "due": None, "description": None, + "completed": None, }, ] } diff --git a/tests/components/tuya/conftest.py b/tests/components/tuya/conftest.py index 21e558b7192d1..fe5782ef27545 100644 --- a/tests/components/tuya/conftest.py +++ b/tests/components/tuya/conftest.py @@ -192,7 +192,7 @@ async def _create_device(hass: HomeAssistant, mock_device_code: str) -> Customer device.function = { key: DeviceFunction( - code=value.get("code"), + code=key, type=value["type"], values=json_dumps(value["value"]), ) @@ -200,7 +200,7 @@ async def _create_device(hass: HomeAssistant, mock_device_code: str) -> Customer } device.status_range = { key: DeviceStatusRange( - code=value.get("code"), + code=key, type=value["type"], values=json_dumps(value["value"]), ) diff --git a/tests/components/tuya/fixtures/mal_gyitctrjj1kefxp2.json b/tests/components/tuya/fixtures/mal_gyitctrjj1kefxp2.json index ee69a811a929a..89cf233aa2023 100644 --- a/tests/components/tuya/fixtures/mal_gyitctrjj1kefxp2.json +++ b/tests/components/tuya/fixtures/mal_gyitctrjj1kefxp2.json @@ -211,7 +211,7 @@ "switch_kb_light": false, "telnet_state": "sim_card_no", "muffling": false, - "alarm_msg": "AFMAZQBuAHMAbwByACAATABvAHcAIABCAGEAdAB0AGUAcgB5AAoAWgBvAG4AZQA6ADAAMAA1AEUAbgB0AHIAYQBuAGMAZQ==", + "alarm_msg": "**REDACTED**", "switch_alarm_propel": true, "alarm_delay_time": 20, "master_state": "normal", diff --git a/tests/components/tuya/fixtures/sp_6bmk1remyscwyx6i.json b/tests/components/tuya/fixtures/sp_6bmk1remyscwyx6i.json index ca5a8dff998d2..dd5cbcc6505c5 100644 --- a/tests/components/tuya/fixtures/sp_6bmk1remyscwyx6i.json +++ b/tests/components/tuya/fixtures/sp_6bmk1remyscwyx6i.json @@ -217,7 +217,7 @@ "wireless_lowpower": 10, "wireless_awake": false, "pir_switch": 3, - "doorbell_pic": "", + "doorbell_pic": "**REDACTED**", "basic_device_volume": 51, "humanoid_filter": false, "alarm_message": "**REDACTED**", diff --git a/tests/components/tuya/fixtures/sp_csr2fqitalj5o0tq.json b/tests/components/tuya/fixtures/sp_csr2fqitalj5o0tq.json index aa29a507162b9..38a8d03ed8641 100644 --- a/tests/components/tuya/fixtures/sp_csr2fqitalj5o0tq.json +++ b/tests/components/tuya/fixtures/sp_csr2fqitalj5o0tq.json @@ -120,8 +120,8 @@ "sd_format_state": 0, "motion_switch": false, "doorbell_active": "", - "doorbell_pic": "aHR0cHM6Ly90eS1ldS1zdG9yYWdlMzAtcGljLnMzLmV1LWNlbnRyYWwtMS5hbWF6b25hd3MuY29tL2U0ODYwMy0yMjU2NjYxOC1zempzYjU0ZDE2ZGI0ZTQ3OTAxYS9kZXRlY3QvMTc2MjE5OTIyMS5qcGVnP1gtQW16LVNlY3VyaXR5LVRva2VuPUZ3b0daWEl2WVhkekVLMyUyRiUyRiUyRiUyRiUyRiUyRiUyRiUyRiUyRiUyRndFYURDUmJiZDNWWldORmtsWUliQ0tDQXZCZCUyQnEwY2EzRURkZzdONTJqUDhmWUI3WVNSS0huSDNnRXZDRjh6OHpMSU92bkZrdG1UQWFLVldSNkxsMDlMMTJ6b09wR2ptekwwRGIyR1NRSG1uSmJNZXRhSm9nWlRQeGI4eGdMbTRwVkhidTkyZndib29UVVllMUwycmhNJTJCdiUyQkFtVG9DTVdwWE9sNThXUDVwZDAwSmdIWGlBUzVGWnhndVR5UWNJcmxFeG5JeW4wYzgwa0VRMjlVa3d2VThMRVpDeUtwTFlIRjJlYTElMkYlMkZPaUk2b1hrdVF3TU0lMkZCWHMlMkJYMWVYYWdnJTJGaW1oRUVhJTJCQ1REODUlMkYlMkZlSHVqZm1KRSUyQnIyeERkdmgwSUJPTFMwYWc1Zm9EbyUyRjZpRHpXMHNKZE1tTjdPNVhiMnMwRnM4MUxwWG5wTXdKRFRxbUklMkJFSDVyYzlxT0NHemY1SUZqbnZZMGF3TjY1blVsMWlpeWphVElCaklwZWVva2htU1F6WlBVJTJGdERzRHlGYUJRRXFWNjkyemlGdVluWHozdnlqdHlzOU5JWG1aJTJGd1hRaTglM0QmWC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BU0lBVVRQTVVKSkpRTlVFUEozSCUyRjIwMjUxMTAzJTJGZXUtY2VudHJhbC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MTEwM1QxOTQ3MDRaJlgtQW16LUV4cGlyZXM9NjAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmJ1Y2tldD10eS1ldS1zdG9yYWdlMzAtcGljJnY9MS4wJlgtQW16LVNpZ25hdHVyZT05YTFlZTYyNWVlMGM5NmQ5NzViMjg2OGQxOGNlOTA3YzU0YTExNjgyNGMzZjkwYzI3YTlmNTNjYjNhN2E0MjA0", + "doorbell_pic": "**REDACTED**", "device_restart": false, - "alarm_message": "eyJ2IjoiNS4wIiwiZmlsZXMiOlt7ImRhdGEiOiJhMThiNDM0YmJmZDY1NGM3N2UzNTc2MWRlMDgyZTc2OGZjM2JmYmQ2NThlZDAyMGIwZGJhZjQ2OTE1YTEwY2NjZDI5YjUxZTY1YjBkNjJiMzAxNmVlZDU0YjU1MTU1ZjE1NzkwNTk2ZDc2YzgwYWFlOWU3ODQ0N2QwYzFlOWNmNmIzMWRlN2ZiOWQyOWU4ZWEwODhlYzAxOGJhYTRhNWMzZjBlMDFmYThiOTRiNGQzYWVkNDk4ZGIwOTUyOTc1ZWQ5ODY2OTNlNmM1NDMyYWY3YTE5N2FiYTA3ZWE3YjJkZGNmZDRjMzQ2N2Q5ZDAwMmJkMDc4OWQ0OTYzNWI1NzkyIiwia2V5SWQiOiJkZWZhdWx0IiwiaXYiOiJjN2JiMTk2Mjc1MWRmOThhZWRiM2VjMGU3Mjk4MWVmMCJ9XSwiY21kIjoiaXBjX2Rvb3JiZWxsIiwidHlwZSI6ImltYWdlIn0=" + "alarm_message": "**REDACTED**" } } diff --git a/tests/components/tuya/fixtures/sp_sdd5f5f2dl5wydjf.json b/tests/components/tuya/fixtures/sp_sdd5f5f2dl5wydjf.json index e98e38b21c8dd..71f0c3fee39c9 100644 --- a/tests/components/tuya/fixtures/sp_sdd5f5f2dl5wydjf.json +++ b/tests/components/tuya/fixtures/sp_sdd5f5f2dl5wydjf.json @@ -364,7 +364,7 @@ "record_switch": true, "record_mode": 1, "pir_switch": 2, - "doorbell_pic": "", + "doorbell_pic": "**REDACTED**", "siren_switch": false, "basic_device_volume": 1, "motion_tracking": true, diff --git a/tests/components/tuya/snapshots/test_diagnostics.ambr b/tests/components/tuya/snapshots/test_diagnostics.ambr index 18e80a2eff1e5..7af44a1e3471b 100644 --- a/tests/components/tuya/snapshots/test_diagnostics.ambr +++ b/tests/components/tuya/snapshots/test_diagnostics.ambr @@ -1,4 +1,305 @@ # serializer version: 1 +# name: test_device_diagnostics[mal_gyitctrjj1kefxp2] + dict({ + 'active_time': '2024-12-02T20:08:56+00:00', + 'category': 'mal', + 'create_time': '2024-12-02T20:08:56+00:00', + 'disabled_by': None, + 'disabled_polling': False, + 'endpoint': 'https://apigw.tuyaeu.com', + 'function': dict({ + 'alarm_delay_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":999,"scale":0,"step":1}', + }), + 'alarm_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"min","min":0,"max":999,"scale":0,"step":1}', + }), + 'delay_set': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":999,"scale":0,"step":1}', + }), + 'master_mode': dict({ + 'type': 'Enum', + 'value': '{"range":["disarmed","arm","home","sos"]}', + }), + 'master_state': dict({ + 'type': 'Enum', + 'value': '{"range":["normal","alarm"]}', + }), + 'muffling': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'sub_admin': dict({ + 'type': 'Raw', + 'value': '{}', + }), + 'sub_class': dict({ + 'type': 'Enum', + 'value': '{"range":["remote_controller","detector"]}', + }), + 'switch_alarm_light': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_alarm_propel': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_alarm_sound': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_kb_light': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_kb_sound': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_mode_sound': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + }), + 'home_assistant': dict({ + 'disabled': False, + 'disabled_by': None, + 'entities': list([ + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'entity_category': None, + 'icon': None, + 'original_device_class': None, + 'original_icon': None, + 'state': dict({ + 'attributes': dict({ + 'changed_by': None, + 'code_arm_required': False, + 'code_format': None, + 'friendly_name': 'Multifunction alarm', + 'supported_features': 11, + }), + 'entity_id': 'alarm_control_panel.multifunction_alarm', + 'state': 'disarmed', + }), + 'unit_of_measurement': None, + }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'entity_category': 'config', + 'icon': None, + 'original_device_class': 'duration', + 'original_icon': None, + 'state': dict({ + 'attributes': dict({ + 'device_class': 'duration', + 'friendly_name': 'Multifunction alarm Arm delay', + 'max': 999.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + 'unit_of_measurement': 's', + }), + 'entity_id': 'number.multifunction_alarm_arm_delay', + 'state': '15.0', + }), + 'unit_of_measurement': 's', + }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'entity_category': 'config', + 'icon': None, + 'original_device_class': 'duration', + 'original_icon': None, + 'state': dict({ + 'attributes': dict({ + 'device_class': 'duration', + 'friendly_name': 'Multifunction alarm Alarm delay', + 'max': 999.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + 'unit_of_measurement': 's', + }), + 'entity_id': 'number.multifunction_alarm_alarm_delay', + 'state': '20.0', + }), + 'unit_of_measurement': 's', + }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'entity_category': 'config', + 'icon': None, + 'original_device_class': 'duration', + 'original_icon': None, + 'state': dict({ + 'attributes': dict({ + 'device_class': 'duration', + 'friendly_name': 'Multifunction alarm Siren duration', + 'max': 999.0, + 'min': 0.0, + 'mode': 'auto', + 'step': 1.0, + 'unit_of_measurement': 'min', + }), + 'entity_id': 'number.multifunction_alarm_siren_duration', + 'state': '3.0', + }), + 'unit_of_measurement': 'min', + }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'entity_category': 'config', + 'icon': None, + 'original_device_class': None, + 'original_icon': None, + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Multifunction alarm Arm beep', + }), + 'entity_id': 'switch.multifunction_alarm_arm_beep', + 'state': 'on', + }), + 'unit_of_measurement': None, + }), + dict({ + 'device_class': None, + 'disabled': False, + 'disabled_by': None, + 'entity_category': 'config', + 'icon': None, + 'original_device_class': None, + 'original_icon': None, + 'state': dict({ + 'attributes': dict({ + 'friendly_name': 'Multifunction alarm Siren', + }), + 'entity_id': 'switch.multifunction_alarm_siren', + 'state': 'on', + }), + 'unit_of_measurement': None, + }), + ]), + 'name': 'Multifunction alarm', + 'name_by_user': None, + }), + 'id': '2pxfek1jjrtctiyglam', + 'mqtt_connected': True, + 'name': 'Multifunction alarm', + 'online': True, + 'product_id': 'gyitctrjj1kefxp2', + 'product_name': 'Multifunction alarm', + 'set_up': True, + 'status': dict({ + 'alarm_delay_time': 20, + 'alarm_msg': '**REDACTED**', + 'alarm_time': 3, + 'delay_set': 15, + 'master_mode': 'disarmed', + 'master_state': 'normal', + 'muffling': False, + 'sub_admin': 'AgEFCggC////HABLAGkAdABjAGgAZQBuACAAUwBtAG8AawBlACBjAAL///8gAHUAbgBkAGUAbABlAHQAYQBiAGwAZQA6AEUATwBMADFkAAL///8gAHUAbgBkAGUAbABlAHQAYQBiAGwAZQA6AEUATwBMADJlAAL///8gAHUAbgBkAGUAbABlAHQAYQBiAGwAZQA6AEUATwBMADNmAAL///8gAHUAbgBkAGUAbABlAHQAYQBiAGwAZQA6AEUATwBMADQ=', + 'sub_class': 'remote_controller', + 'sub_state': 'normal', + 'switch_alarm_light': True, + 'switch_alarm_propel': True, + 'switch_alarm_sound': True, + 'switch_kb_light': False, + 'switch_kb_sound': False, + 'switch_mode_sound': True, + 'telnet_state': 'sim_card_no', + }), + 'status_range': dict({ + 'alarm_delay_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":999,"scale":0,"step":1}', + }), + 'alarm_msg': dict({ + 'type': 'Raw', + 'value': '{}', + }), + 'alarm_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"min","min":0,"max":999,"scale":0,"step":1}', + }), + 'delay_set': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":999,"scale":0,"step":1}', + }), + 'master_mode': dict({ + 'type': 'Enum', + 'value': '{"range":["disarmed","arm","home","sos"]}', + }), + 'master_state': dict({ + 'type': 'Enum', + 'value': '{"range":["normal","alarm"]}', + }), + 'muffling': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'sub_admin': dict({ + 'type': 'Raw', + 'value': '{}', + }), + 'sub_class': dict({ + 'type': 'Enum', + 'value': '{"range":["remote_controller","detector"]}', + }), + 'sub_state': dict({ + 'type': 'Enum', + 'value': '{"range":["normal","alarm","fault","others"]}', + }), + 'switch_alarm_light': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_alarm_propel': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_alarm_sound': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_kb_light': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_kb_sound': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'switch_mode_sound': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'telnet_state': dict({ + 'type': 'Enum', + 'value': '{"range":["normal","network_no","phone_no","sim_card_no","network_search","signal_level_1","signal_level_2","signal_level_3","signal_level_4","signal_level_5"]}', + }), + }), + 'sub': False, + 'support_local': True, + 'terminal_id': '7cd96aff-6ec8-4006-b093-3dbff7947591', + 'time_zone': '+02:00', + 'update_time': '2024-12-02T20:08:56+00:00', + }) +# --- # name: test_device_diagnostics[rqbj_4iqe2hsfyd86kwwc] dict({ 'active_time': '2025-06-24T20:33:10+00:00', @@ -8,7 +309,15 @@ 'disabled_polling': False, 'endpoint': 'https://apigw.tuyaeu.com', 'function': dict({ - 'null': dict({ + 'alarm_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":3600,"scale":0,"step":1}', + }), + 'muffling': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'self_checking': dict({ 'type': 'Boolean', 'value': '{}', }), @@ -74,7 +383,27 @@ 'self_checking': False, }), 'status_range': dict({ - 'null': dict({ + 'alarm_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":3600,"scale":0,"step":1}', + }), + 'checking_result': dict({ + 'type': 'Enum', + 'value': '{"range":["checking","check_success","check_failure","others"]}', + }), + 'gas_sensor_status': dict({ + 'type': 'Enum', + 'value': '{"range":["alarm","normal"]}', + }), + 'gas_sensor_value': dict({ + 'type': 'Integer', + 'value': '{"unit":"ppm","min":0,"max":999,"scale":0,"step":1}', + }), + 'muffling': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'self_checking': dict({ 'type': 'Boolean', 'value': '{}', }), @@ -94,7 +423,15 @@ 'category': 'rqbj', 'create_time': '2025-06-24T20:33:10+00:00', 'function': dict({ - 'null': dict({ + 'alarm_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":3600,"scale":0,"step":1}', + }), + 'muffling': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'self_checking': dict({ 'type': 'Boolean', 'value': '{}', }), @@ -159,7 +496,27 @@ 'self_checking': False, }), 'status_range': dict({ - 'null': dict({ + 'alarm_time': dict({ + 'type': 'Integer', + 'value': '{"unit":"s","min":0,"max":3600,"scale":0,"step":1}', + }), + 'checking_result': dict({ + 'type': 'Enum', + 'value': '{"range":["checking","check_success","check_failure","others"]}', + }), + 'gas_sensor_status': dict({ + 'type': 'Enum', + 'value': '{"range":["alarm","normal"]}', + }), + 'gas_sensor_value': dict({ + 'type': 'Integer', + 'value': '{"unit":"ppm","min":0,"max":999,"scale":0,"step":1}', + }), + 'muffling': dict({ + 'type': 'Boolean', + 'value': '{}', + }), + 'self_checking': dict({ 'type': 'Boolean', 'value': '{}', }), diff --git a/tests/components/tuya/test_alarm_control_panel.py b/tests/components/tuya/test_alarm_control_panel.py index 5e10bca07ae13..f4274f794b1d2 100644 --- a/tests/components/tuya/test_alarm_control_panel.py +++ b/tests/components/tuya/test_alarm_control_panel.py @@ -80,54 +80,63 @@ async def test_service( mock_manager.send_commands.assert_called_once_with(mock_device.id, [command]) +@patch("homeassistant.components.tuya.PLATFORMS", [Platform.ALARM_CONTROL_PANEL]) @pytest.mark.parametrize( "mock_device_code", ["mal_gyitctrjj1kefxp2"], ) -async def test_alarm_state_triggered( - hass: HomeAssistant, - mock_manager: Manager, - mock_config_entry: MockConfigEntry, - mock_device: CustomerDevice, -) -> None: - """Test alarm state returns TRIGGERED for non-battery alarms.""" - entity_id = "alarm_control_panel.multifunction_alarm" - - # Set up alarm state without battery warning - mock_device.status["master_state"] = "alarm" - mock_device.status["alarm_msg"] = ( - "AFQAZQBzAHQAIABTAGUAbgBzAG8Acg==" # "Test Sensor" in UTF-16BE - ) - - await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) - - state = hass.states.get(entity_id) - assert state is not None, f"{entity_id} does not exist" - assert state.state == AlarmControlPanelState.TRIGGERED - - @pytest.mark.parametrize( - "mock_device_code", - ["mal_gyitctrjj1kefxp2"], + ("status_updates", "expected_state"), + [ + ( + {"master_mode": "disarmed"}, + AlarmControlPanelState.DISARMED, + ), + ( + {"master_mode": "arm"}, + AlarmControlPanelState.ARMED_AWAY, + ), + ( + {"master_mode": "home"}, + AlarmControlPanelState.ARMED_HOME, + ), + ( + {"master_mode": "sos"}, + AlarmControlPanelState.TRIGGERED, + ), + ( + { + "master_mode": "home", + "master_state": "alarm", + # "Test Sensor" in UTF-16BE + "alarm_msg": "AFQAZQBzAHQAIABTAGUAbgBzAG8Acg==", + }, + AlarmControlPanelState.TRIGGERED, + ), + ( + { + "master_mode": "home", + "master_state": "alarm", + # "Sensor Low Battery Test Sensor" in UTF-16BE + "alarm_msg": "AFMAZQBuAHMAbwByACAATABvAHcAIABCAGEAdAB0AGUAcgB5ACAAVABlAHMAdAAgAFMAZQBuAHMAbwBy", + }, + AlarmControlPanelState.ARMED_HOME, + ), + ], ) -async def test_alarm_state_battery_warning( +async def test_state( hass: HomeAssistant, mock_manager: Manager, mock_config_entry: MockConfigEntry, mock_device: CustomerDevice, + status_updates: dict[str, Any], + expected_state: str, ) -> None: - """Test alarm state ignores battery warnings.""" + """Test state.""" entity_id = "alarm_control_panel.multifunction_alarm" - - # Set up alarm state with battery warning - mock_device.status["master_state"] = "alarm" - mock_device.status["alarm_msg"] = ( - "AFMAZQBuAHMAbwByACAATABvAHcAIABCAGEAdAB0AGUAcgB5ACAAVABlAHMAdAAgAFMAZQBuAHMAbwBy" # "Sensor Low Battery Test Sensor" in UTF-16BE - ) - + mock_device.status.update(status_updates) await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) state = hass.states.get(entity_id) assert state is not None, f"{entity_id} does not exist" - # Should not be triggered for battery warnings - assert state.state != AlarmControlPanelState.TRIGGERED + assert state.state == expected_state diff --git a/tests/components/tuya/test_camera.py b/tests/components/tuya/test_camera.py index 4c2dc5e35cae8..35c0cd12a4a11 100644 --- a/tests/components/tuya/test_camera.py +++ b/tests/components/tuya/test_camera.py @@ -2,13 +2,19 @@ from __future__ import annotations +from typing import Any from unittest.mock import patch import pytest from syrupy.assertion import SnapshotAssertion from tuya_sharing import CustomerDevice, Manager -from homeassistant.const import Platform +from homeassistant.components.camera import ( + DOMAIN as CAMERA_DOMAIN, + SERVICE_DISABLE_MOTION, + SERVICE_ENABLE_MOTION, +) +from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -46,3 +52,48 @@ async def test_platform_setup_and_discovery( snapshot, mock_config_entry.entry_id, ) + + +@patch("homeassistant.components.tuya.PLATFORMS", [Platform.CAMERA]) +@pytest.mark.parametrize( + "mock_device_code", + ["sp_rudejjigkywujjvs"], +) +@pytest.mark.parametrize( + ("service", "expected_command"), + [ + ( + SERVICE_DISABLE_MOTION, + {"code": "motion_switch", "value": False}, + ), + ( + SERVICE_ENABLE_MOTION, + {"code": "motion_switch", "value": True}, + ), + ], +) +async def test_motion_detection( + hass: HomeAssistant, + mock_manager: Manager, + mock_config_entry: MockConfigEntry, + mock_device: CustomerDevice, + service: str, + expected_command: dict[str, Any], +) -> None: + """Test turning off a switch.""" + entity_id = "camera.burocam" + await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) + + state = hass.states.get(entity_id) + assert state is not None, f"{entity_id} does not exist" + await hass.services.async_call( + CAMERA_DOMAIN, + service, + { + ATTR_ENTITY_ID: entity_id, + }, + blocking=True, + ) + mock_manager.send_commands.assert_called_once_with( + mock_device.id, [expected_command] + ) diff --git a/tests/components/tuya/test_climate.py b/tests/components/tuya/test_climate.py index 769078361f862..ce81a504699e1 100644 --- a/tests/components/tuya/test_climate.py +++ b/tests/components/tuya/test_climate.py @@ -68,7 +68,7 @@ async def test_set_temperature( blocking=True, ) mock_manager.send_commands.assert_called_once_with( - mock_device.id, [{"code": "temp_set", "value": 22}] + mock_device.id, [{"code": "temp_set", "value": 23}] ) diff --git a/tests/components/tuya/test_diagnostics.py b/tests/components/tuya/test_diagnostics.py index aff84edf23148..0aadbf455322c 100644 --- a/tests/components/tuya/test_diagnostics.py +++ b/tests/components/tuya/test_diagnostics.py @@ -42,7 +42,13 @@ async def test_entry_diagnostics( ) -@pytest.mark.parametrize("mock_device_code", ["rqbj_4iqe2hsfyd86kwwc"]) +@pytest.mark.parametrize( + "mock_device_code", + [ + "mal_gyitctrjj1kefxp2", + "rqbj_4iqe2hsfyd86kwwc", + ], +) async def test_device_diagnostics( hass: HomeAssistant, mock_manager: Manager, diff --git a/tests/components/tuya/test_init.py b/tests/components/tuya/test_init.py index a3ac054902fe0..9bd08a746a1f0 100644 --- a/tests/components/tuya/test_init.py +++ b/tests/components/tuya/test_init.py @@ -6,6 +6,7 @@ from tuya_sharing import CustomerDevice, Manager from homeassistant.components.tuya.const import DOMAIN +from homeassistant.components.tuya.diagnostics import _REDACTED_DPCODES from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er @@ -65,3 +66,11 @@ async def test_fixtures_valid(hass: HomeAssistant) -> None: assert key not in details, ( f"Please remove data[`'{key}']` from {device_code}.json" ) + if "status" in details: + statuses = details["status"] + for key in statuses: + if key in _REDACTED_DPCODES: + assert statuses[key] == "**REDACTED**", ( + f"Please mark `data['status']['{key}']` as `**REDACTED**`" + f" in {device_code}.json" + ) diff --git a/tests/components/velux/snapshots/test_cover.ambr b/tests/components/velux/snapshots/test_cover.ambr new file mode 100644 index 0000000000000..93001bd95456b --- /dev/null +++ b/tests/components/velux/snapshots/test_cover.ambr @@ -0,0 +1,52 @@ +# serializer version: 1 +# name: test_cover_setup[cover.test_window-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'cover', + 'entity_category': None, + 'entity_id': 'cover.test_window', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'velux', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': '123456789', + 'unit_of_measurement': None, + }) +# --- +# name: test_cover_setup[cover.test_window-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'current_position': 70, + 'device_class': 'window', + 'friendly_name': 'Test Window', + 'supported_features': , + }), + 'context': , + 'entity_id': 'cover.test_window', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'open', + }) +# --- diff --git a/tests/components/velux/test_cover.py b/tests/components/velux/test_cover.py index a77ddbfa7479f..bd16b88b5f06b 100644 --- a/tests/components/velux/test_cover.py +++ b/tests/components/velux/test_cover.py @@ -1,32 +1,69 @@ """Tests for the Velux cover platform.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock -from freezegun.api import FrozenDateTimeFactory import pytest +from homeassistant.components.velux import DOMAIN from homeassistant.const import STATE_CLOSED, STATE_OPEN, Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr, entity_registry as er from . import update_callback_entity -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, SnapshotAssertion, snapshot_platform -@pytest.mark.usefixtures("mock_pyvlx") +@pytest.fixture +def platform() -> Platform: + """Fixture to specify platform to test.""" + return Platform.COVER + + +@pytest.mark.usefixtures("setup_integration") +async def test_cover_setup( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, + device_registry: dr.DeviceRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Snapshot the cover entity (registry + state).""" + await snapshot_platform( + hass, + entity_registry, + snapshot, + mock_config_entry.entry_id, + ) + + # Get the cover entity setup and test device association + entity_entries = er.async_entries_for_config_entry( + entity_registry, mock_config_entry.entry_id + ) + assert len(entity_entries) == 1 + entry = entity_entries[0] + + assert entry.device_id is not None + device_entry = device_registry.async_get(entry.device_id) + assert device_entry is not None + assert (DOMAIN, f"{123456789}") in device_entry.identifiers + assert device_entry.via_device_id is not None + via_device_entry = device_registry.async_get(device_entry.via_device_id) + assert via_device_entry is not None + assert ( + DOMAIN, + f"gateway_{mock_config_entry.entry_id}", + ) in via_device_entry.identifiers + + +@pytest.mark.usefixtures("setup_integration") async def test_cover_closed( hass: HomeAssistant, mock_window: AsyncMock, mock_config_entry: MockConfigEntry, - freezer: FrozenDateTimeFactory, ) -> None: """Test the cover closed state.""" - mock_config_entry.add_to_hass(hass) - with patch("homeassistant.components.velux.PLATFORMS", [Platform.COVER]): - assert await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() - test_entity_id = "cover.test_window" # Initial state should be open diff --git a/tests/components/vicare/snapshots/test_sensor.ambr b/tests/components/vicare/snapshots/test_sensor.ambr index 8a1bc268fa9f7..bf16b3c32b807 100644 --- a/tests/components/vicare/snapshots/test_sensor.ambr +++ b/tests/components/vicare/snapshots/test_sensor.ambr @@ -3283,7 +3283,7 @@ 'state': '5067', }) # --- -# name: test_all_entities[sensor.model2_condensor_subcooling_temperature-entry] +# name: test_all_entities[sensor.model2_condenser_subcooling_temperature-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ }), @@ -3296,7 +3296,7 @@ 'disabled_by': None, 'domain': 'sensor', 'entity_category': None, - 'entity_id': 'sensor.model2_condensor_subcooling_temperature', + 'entity_id': 'sensor.model2_condenser_subcooling_temperature', 'has_entity_name': True, 'hidden_by': None, 'icon': None, @@ -3311,25 +3311,25 @@ }), 'original_device_class': , 'original_icon': None, - 'original_name': 'Condensor subcooling temperature', + 'original_name': 'Condenser subcooling temperature', 'platform': 'vicare', 'previous_unique_id': None, 'suggested_object_id': None, 'supported_features': 0, - 'translation_key': 'condensor_subcooling_temperature', - 'unique_id': 'gateway2_################-condensor_subcooling_temperature-0', + 'translation_key': 'condenser_subcooling_temperature', + 'unique_id': 'gateway2_################-condenser_subcooling_temperature-0', 'unit_of_measurement': , }) # --- -# name: test_all_entities[sensor.model2_condensor_subcooling_temperature-state] +# name: test_all_entities[sensor.model2_condenser_subcooling_temperature-state] StateSnapshot({ 'attributes': ReadOnlyDict({ 'device_class': 'temperature', - 'friendly_name': 'model2 Condensor subcooling temperature', + 'friendly_name': 'model2 Condenser subcooling temperature', 'unit_of_measurement': , }), 'context': , - 'entity_id': 'sensor.model2_condensor_subcooling_temperature', + 'entity_id': 'sensor.model2_condenser_subcooling_temperature', 'last_changed': , 'last_reported': , 'last_updated': , diff --git a/tests/components/xbox/snapshots/test_media_player.ambr b/tests/components/xbox/snapshots/test_media_player.ambr index 3802c537ad6ad..03b5b22e14261 100644 --- a/tests/components/xbox/snapshots/test_media_player.ambr +++ b/tests/components/xbox/snapshots/test_media_player.ambr @@ -41,6 +41,7 @@ 'entity_picture': 'http://store-images.s-microsoft.com/image/apps.9815.9007199266246365.7dc5d343-fe4a-40c3-93dd-c78e77f97331.45eebdef-f725-4799-bbf8-9ad8391a8279', 'entity_picture_local': '/api/media_player_proxy/media_player.xone?token=mock_token&cache=739260d7bff66329', 'friendly_name': 'XONE', + 'media_content_id': '9WZDNCRFJ3TJ', 'media_content_type': , 'media_title': 'Netflix', 'supported_features': , @@ -95,6 +96,7 @@ 'entity_picture': 'http://store-images.s-microsoft.com/image/apps.9815.9007199266246365.7dc5d343-fe4a-40c3-93dd-c78e77f97331.45eebdef-f725-4799-bbf8-9ad8391a8279', 'entity_picture_local': '/api/media_player_proxy/media_player.xonex?token=mock_token&cache=739260d7bff66329', 'friendly_name': 'XONEX', + 'media_content_id': '9WZDNCRFJ3TJ', 'media_content_type': , 'media_title': 'Netflix', 'supported_features': ,