Add IQ Meter Collar and C6 Combiner to enphase_envoy integration (#150649)

This commit is contained in:
Arie Catsman
2025-08-14 22:34:37 +02:00
committed by GitHub
parent 9c21965a34
commit 7e6ceee9d1
7 changed files with 631 additions and 1 deletions

View File

@@ -6,7 +6,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from operator import attrgetter from operator import attrgetter
from pyenphase import EnvoyEncharge, EnvoyEnpower from pyenphase import EnvoyC6CC, EnvoyCollar, EnvoyEncharge, EnvoyEnpower
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
@@ -72,6 +72,42 @@ ENPOWER_SENSORS = (
) )
@dataclass(frozen=True, kw_only=True)
class EnvoyCollarBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes an Envoy IQ Meter Collar binary sensor entity."""
value_fn: Callable[[EnvoyCollar], bool]
COLLAR_SENSORS = (
EnvoyCollarBinarySensorEntityDescription(
key="communicating",
translation_key="communicating",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=attrgetter("communicating"),
),
)
@dataclass(frozen=True, kw_only=True)
class EnvoyC6CCBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Describes an C6 Combiner controller binary sensor entity."""
value_fn: Callable[[EnvoyC6CC], bool]
C6CC_SENSORS = (
EnvoyC6CCBinarySensorEntityDescription(
key="communicating",
translation_key="communicating",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=attrgetter("communicating"),
),
)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: EnphaseConfigEntry, config_entry: EnphaseConfigEntry,
@@ -95,6 +131,18 @@ async def async_setup_entry(
for description in ENPOWER_SENSORS for description in ENPOWER_SENSORS
) )
if envoy_data.collar:
entities.extend(
EnvoyCollarBinarySensorEntity(coordinator, description)
for description in COLLAR_SENSORS
)
if envoy_data.c6cc:
entities.extend(
EnvoyC6CCBinarySensorEntity(coordinator, description)
for description in C6CC_SENSORS
)
async_add_entities(entities) async_add_entities(entities)
@@ -168,3 +216,69 @@ class EnvoyEnpowerBinarySensorEntity(EnvoyBaseBinarySensorEntity):
enpower = self.data.enpower enpower = self.data.enpower
assert enpower is not None assert enpower is not None
return self.entity_description.value_fn(enpower) return self.entity_description.value_fn(enpower)
class EnvoyCollarBinarySensorEntity(EnvoyBaseBinarySensorEntity):
"""Defines an IQ Meter Collar binary_sensor entity."""
entity_description: EnvoyCollarBinarySensorEntityDescription
def __init__(
self,
coordinator: EnphaseUpdateCoordinator,
description: EnvoyCollarBinarySensorEntityDescription,
) -> None:
"""Init the Collar base entity."""
super().__init__(coordinator, description)
collar_data = self.data.collar
assert collar_data is not None
self._attr_unique_id = f"{collar_data.serial_number}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, collar_data.serial_number)},
manufacturer="Enphase",
model="IQ Meter Collar",
name=f"Collar {collar_data.serial_number}",
sw_version=str(collar_data.firmware_version),
via_device=(DOMAIN, self.envoy_serial_num),
serial_number=collar_data.serial_number,
)
@property
def is_on(self) -> bool:
"""Return the state of the Collar binary_sensor."""
collar_data = self.data.collar
assert collar_data is not None
return self.entity_description.value_fn(collar_data)
class EnvoyC6CCBinarySensorEntity(EnvoyBaseBinarySensorEntity):
"""Defines an C6 Combiner binary_sensor entity."""
entity_description: EnvoyC6CCBinarySensorEntityDescription
def __init__(
self,
coordinator: EnphaseUpdateCoordinator,
description: EnvoyC6CCBinarySensorEntityDescription,
) -> None:
"""Init the C6 Combiner base entity."""
super().__init__(coordinator, description)
c6cc_data = self.data.c6cc
assert c6cc_data is not None
self._attr_unique_id = f"{c6cc_data.serial_number}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, c6cc_data.serial_number)},
manufacturer="Enphase",
model="C6 COMBINER CONTROLLER",
name=f"C6 Combiner {c6cc_data.serial_number}",
sw_version=str(c6cc_data.firmware_version),
via_device=(DOMAIN, self.envoy_serial_num),
serial_number=c6cc_data.serial_number,
)
@property
def is_on(self) -> bool:
"""Return the state of the C6 Combiner binary_sensor."""
c6cc_data = self.data.c6cc
assert c6cc_data is not None
return self.entity_description.value_fn(c6cc_data)

View File

@@ -12,6 +12,8 @@ from typing import TYPE_CHECKING
from pyenphase import ( from pyenphase import (
EnvoyACBPower, EnvoyACBPower,
EnvoyBatteryAggregate, EnvoyBatteryAggregate,
EnvoyC6CC,
EnvoyCollar,
EnvoyEncharge, EnvoyEncharge,
EnvoyEnchargeAggregate, EnvoyEnchargeAggregate,
EnvoyEnchargePower, EnvoyEnchargePower,
@@ -790,6 +792,58 @@ ENPOWER_SENSORS = (
) )
@dataclass(frozen=True, kw_only=True)
class EnvoyCollarSensorEntityDescription(SensorEntityDescription):
"""Describes an Envoy Collar sensor entity."""
value_fn: Callable[[EnvoyCollar], datetime.datetime | int | float | str]
COLLAR_SENSORS = (
EnvoyCollarSensorEntityDescription(
key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
value_fn=attrgetter("temperature"),
),
EnvoyCollarSensorEntityDescription(
key=LAST_REPORTED_KEY,
translation_key=LAST_REPORTED_KEY,
native_unit_of_measurement=None,
device_class=SensorDeviceClass.TIMESTAMP,
value_fn=lambda collar: dt_util.utc_from_timestamp(collar.last_report_date),
),
EnvoyCollarSensorEntityDescription(
key="grid_state",
translation_key="grid_status",
value_fn=lambda collar: collar.grid_state,
),
EnvoyCollarSensorEntityDescription(
key="mid_state",
translation_key="mid_state",
value_fn=lambda collar: collar.mid_state,
),
)
@dataclass(frozen=True, kw_only=True)
class EnvoyC6CCSensorEntityDescription(SensorEntityDescription):
"""Describes an Envoy C6 Combiner controller sensor entity."""
value_fn: Callable[[EnvoyC6CC], datetime.datetime]
C6CC_SENSORS = (
EnvoyC6CCSensorEntityDescription(
key=LAST_REPORTED_KEY,
translation_key=LAST_REPORTED_KEY,
native_unit_of_measurement=None,
device_class=SensorDeviceClass.TIMESTAMP,
value_fn=lambda c6cc: dt_util.utc_from_timestamp(c6cc.last_report_date),
),
)
@dataclass(frozen=True) @dataclass(frozen=True)
class EnvoyEnchargeAggregateRequiredKeysMixin: class EnvoyEnchargeAggregateRequiredKeysMixin:
"""Mixin for required keys.""" """Mixin for required keys."""
@@ -1050,6 +1104,15 @@ async def async_setup_entry(
AggregateBatteryEntity(coordinator, description) AggregateBatteryEntity(coordinator, description)
for description in AGGREGATE_BATTERY_SENSORS for description in AGGREGATE_BATTERY_SENSORS
) )
if envoy_data.collar:
entities.extend(
EnvoyCollarEntity(coordinator, description)
for description in COLLAR_SENSORS
)
if envoy_data.c6cc:
entities.extend(
EnvoyC6CCEntity(coordinator, description) for description in C6CC_SENSORS
)
async_add_entities(entities) async_add_entities(entities)
@@ -1488,3 +1551,70 @@ class AggregateBatteryEntity(EnvoySystemSensorEntity):
battery_aggregate = self.data.battery_aggregate battery_aggregate = self.data.battery_aggregate
assert battery_aggregate is not None assert battery_aggregate is not None
return self.entity_description.value_fn(battery_aggregate) return self.entity_description.value_fn(battery_aggregate)
class EnvoyCollarEntity(EnvoySensorBaseEntity):
"""Envoy Collar sensor entity."""
entity_description: EnvoyCollarSensorEntityDescription
def __init__(
self,
coordinator: EnphaseUpdateCoordinator,
description: EnvoyCollarSensorEntityDescription,
) -> None:
"""Initialize Collar entity."""
super().__init__(coordinator, description)
collar_data = self.data.collar
assert collar_data is not None
self._serial_number = collar_data.serial_number
self._attr_unique_id = f"{collar_data.serial_number}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, collar_data.serial_number)},
manufacturer="Enphase",
model="IQ Meter Collar",
name=f"Collar {collar_data.serial_number}",
sw_version=str(collar_data.firmware_version),
via_device=(DOMAIN, self.envoy_serial_num),
serial_number=collar_data.serial_number,
)
@property
def native_value(self) -> datetime.datetime | int | float | str:
"""Return the state of the collar sensors."""
collar_data = self.data.collar
assert collar_data is not None
return self.entity_description.value_fn(collar_data)
class EnvoyC6CCEntity(EnvoySensorBaseEntity):
"""Envoy C6CC sensor entity."""
entity_description: EnvoyC6CCSensorEntityDescription
def __init__(
self,
coordinator: EnphaseUpdateCoordinator,
description: EnvoyC6CCSensorEntityDescription,
) -> None:
"""Initialize Encharge entity."""
super().__init__(coordinator, description)
c6cc_data = self.data.c6cc
assert c6cc_data is not None
self._attr_unique_id = f"{c6cc_data.serial_number}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, c6cc_data.serial_number)},
manufacturer="Enphase",
model="C6 COMBINER CONTROLLER",
name=f"C6 Combiner {c6cc_data.serial_number}",
sw_version=str(c6cc_data.firmware_version),
via_device=(DOMAIN, self.envoy_serial_num),
serial_number=c6cc_data.serial_number,
)
@property
def native_value(self) -> datetime.datetime:
"""Return the state of the c6cc inventory sensors."""
c6cc_data = self.data.c6cc
assert c6cc_data is not None
return self.entity_description.value_fn(c6cc_data)

View File

@@ -407,6 +407,12 @@
}, },
"last_report_duration": { "last_report_duration": {
"name": "Last report duration" "name": "Last report duration"
},
"grid_status": {
"name": "[%key:component::enphase_envoy::entity::binary_sensor::grid_status::name%]"
},
"mid_state": {
"name": "MID state"
} }
}, },
"switch": { "switch": {

View File

@@ -9,6 +9,8 @@ import multidict
from pyenphase import ( from pyenphase import (
EnvoyACBPower, EnvoyACBPower,
EnvoyBatteryAggregate, EnvoyBatteryAggregate,
EnvoyC6CC,
EnvoyCollar,
EnvoyData, EnvoyData,
EnvoyEncharge, EnvoyEncharge,
EnvoyEnchargeAggregate, EnvoyEnchargeAggregate,
@@ -260,6 +262,10 @@ def _load_json_2_encharge_enpower_data(
) )
if item := json_fixture["data"].get("battery_aggregate"): if item := json_fixture["data"].get("battery_aggregate"):
mocked_data.battery_aggregate = EnvoyBatteryAggregate(**item) mocked_data.battery_aggregate = EnvoyBatteryAggregate(**item)
if item := json_fixture["data"].get("collar"):
mocked_data.collar = EnvoyCollar(**item)
if item := json_fixture["data"].get("c6cc"):
mocked_data.c6cc = EnvoyC6CC(**item)
def _load_json_2_raw_data(mocked_data: EnvoyData, json_fixture: dict[str, Any]) -> None: def _load_json_2_raw_data(mocked_data: EnvoyData, json_fixture: dict[str, Any]) -> None:

View File

@@ -407,6 +407,35 @@
"type": "NONE" "type": "NONE"
} }
}, },
"collar": {
"admin_state": 88,
"admin_state_str": "ENCMN_MDE_ON_GRID",
"firmware_loaded_date": 1752939759,
"firmware_version": "3.0.6-D0",
"installed_date": 1752939759,
"last_report_date": 1752939759,
"communicating": true,
"mid_state": "close",
"grid_state": "on_grid",
"part_number": "865-00400-r22",
"serial_number": "482520020939",
"temperature": 42,
"temperature_unit": "C",
"control_error": 0,
"collar_state": "Installed"
},
"c6cc": {
"admin_state": 82,
"admin_state_str": "ENCMN_C6_CC_READY",
"firmware_loaded_date": 1752945451,
"firmware_version": "0.1.20-D1",
"installed_date": 1752945451,
"last_report_date": 1752945451,
"communicating": true,
"part_number": "800-02403-r08",
"serial_number": "482523040549",
"dmir_version": "0.1.20-D1"
},
"inverters": { "inverters": {
"1": { "1": {
"serial_number": "1", "serial_number": "1",

View File

@@ -96,6 +96,104 @@
'state': 'on', 'state': 'on',
}) })
# --- # ---
# name: test_binary_sensor[envoy_metered_batt_relay][binary_sensor.c6_combiner_482523040549_communicating-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.c6_combiner_482523040549_communicating',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: '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,
})
# ---
# name: test_binary_sensor[envoy_metered_batt_relay][binary_sensor.c6_combiner_482523040549_communicating-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'C6 Combiner 482523040549 Communicating',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.c6_combiner_482523040549_communicating',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_binary_sensor[envoy_metered_batt_relay][binary_sensor.collar_482520020939_communicating-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'binary_sensor.collar_482520020939_communicating',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <BinarySensorDeviceClass.CONNECTIVITY: '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,
})
# ---
# name: test_binary_sensor[envoy_metered_batt_relay][binary_sensor.collar_482520020939_communicating-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'connectivity',
'friendly_name': 'Collar 482520020939 Communicating',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.collar_482520020939_communicating',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_binary_sensor[envoy_metered_batt_relay][binary_sensor.encharge_123456_communicating-entry] # name: test_binary_sensor[envoy_metered_batt_relay][binary_sensor.encharge_123456_communicating-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@@ -13947,6 +13947,253 @@
'state': 'unknown', 'state': 'unknown',
}) })
# --- # ---
# name: test_sensor[envoy_metered_batt_relay][sensor.c6_combiner_482523040549_last_reported-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'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,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.TIMESTAMP: '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,
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.c6_combiner_482523040549_last_reported-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'C6 Combiner 482523040549 Last reported',
}),
'context': <ANY>,
'entity_id': 'sensor.c6_combiner_482523040549_last_reported',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2025-07-19T17:17:31+00:00',
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_grid_status-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.collar_482520020939_grid_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'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,
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_grid_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Collar 482520020939 Grid status',
}),
'context': <ANY>,
'entity_id': 'sensor.collar_482520020939_grid_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on_grid',
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_last_reported-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.collar_482520020939_last_reported',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.TIMESTAMP: '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,
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_last_reported-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Collar 482520020939 Last reported',
}),
'context': <ANY>,
'entity_id': 'sensor.collar_482520020939_last_reported',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2025-07-19T15:42:39+00:00',
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_mid_state-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.collar_482520020939_mid_state',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'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,
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_mid_state-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Collar 482520020939 MID state',
}),
'context': <ANY>,
'entity_id': 'sensor.collar_482520020939_mid_state',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'close',
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.collar_482520020939_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: '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': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.collar_482520020939_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Collar 482520020939 Temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.collar_482520020939_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '42',
})
# ---
# name: test_sensor[envoy_metered_batt_relay][sensor.encharge_123456_apparent_power-entry] # name: test_sensor[envoy_metered_batt_relay][sensor.encharge_123456_apparent_power-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({