Define entity attributes as entity class variables (#50925)

* Define entity attributes as entity class variables

* Example coronavirus integration

* Example verisure

* Cleanup/typing fixes

* Fix Coronavirus

* Revert "Fix Coronavirus"

This reverts commit 060843860f.

* Revert "Cleanup/typing fixes"

This reverts commit 659b79e75a.

* Define entity attributes as entity class variables (attr alternative)

* Example coronavirus

* Example nut

* Example verisure

* Mark private

* Cleanup after all reverting/cherrypicking/merging

* Implement all entity properties

* Update coronavirus example

* Update nut example

* Update verisure example

* Lets not talk about this one...

* Fix multiple class attribute
This commit is contained in:
Franck Nijhof 2021-05-22 18:13:50 +02:00 committed by GitHub
parent b9a0fb93eb
commit 38d095aa18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 80 additions and 173 deletions

View file

@ -27,17 +27,21 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class CoronavirusSensor(CoordinatorEntity, SensorEntity):
"""Sensor representing corona virus data."""
name = None
unique_id = None
_attr_unit_of_measurement = "people"
def __init__(self, coordinator, country, info_type):
"""Initialize coronavirus sensor."""
super().__init__(coordinator)
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
self._attr_icon = SENSORS[info_type]
self._attr_unique_id = f"{country}-{info_type}"
if country == OPTION_WORLDWIDE:
self.name = f"Worldwide Coronavirus {info_type}"
self._attr_name = f"Worldwide Coronavirus {info_type}"
else:
self.name = f"{coordinator.data[country].country} Coronavirus {info_type}"
self.unique_id = f"{country}-{info_type}"
self._attr_name = (
f"{coordinator.data[country].country} Coronavirus {info_type}"
)
self.country = country
self.info_type = info_type
@ -62,18 +66,3 @@ class CoronavirusSensor(CoordinatorEntity, SensorEntity):
return sum_cases
return getattr(self.coordinator.data[self.country], self.info_type)
@property
def icon(self):
"""Return the icon."""
return SENSORS[self.info_type]
@property
def unit_of_measurement(self):
"""Return unit of measurement."""
return "people"
@property
def extra_state_attributes(self):
"""Return device attributes."""
return {ATTR_ATTRIBUTION: ATTRIBUTION}

View file

@ -98,11 +98,14 @@ class NUTSensor(CoordinatorEntity, SensorEntity):
self._firmware = firmware
self._model = model
self._device_name = name
self._name = f"{name} {SENSOR_TYPES[sensor_type][SENSOR_NAME]}"
self._unit = SENSOR_TYPES[sensor_type][SENSOR_UNIT]
self._data = data
self._unique_id = unique_id
self._attr_device_class = SENSOR_TYPES[self._type][SENSOR_DEVICE_CLASS]
self._attr_icon = SENSOR_TYPES[self._type][SENSOR_ICON]
self._attr_name = f"{name} {SENSOR_TYPES[sensor_type][SENSOR_NAME]}"
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_type][SENSOR_UNIT]
@property
def device_info(self):
"""Device info for the ups."""
@ -127,25 +130,6 @@ class NUTSensor(CoordinatorEntity, SensorEntity):
return None
return f"{self._unique_id}_{self._type}"
@property
def name(self):
"""Return the name of the UPS sensor."""
return self._name
@property
def icon(self):
"""Icon to use in the frontend, if any."""
if SENSOR_TYPES[self._type][SENSOR_DEVICE_CLASS]:
# The UI will assign an icon
# if it has a class
return None
return SENSOR_TYPES[self._type][SENSOR_ICON]
@property
def device_class(self):
"""Device class of the sensor."""
return SENSOR_TYPES[self._type][SENSOR_DEVICE_CLASS]
@property
def state(self):
"""Return entity state from ups."""
@ -155,11 +139,6 @@ class NUTSensor(CoordinatorEntity, SensorEntity):
return _format_display_state(self._data.status)
return self._data.status.get(self._type)
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit
@property
def extra_state_attributes(self):
"""Return the sensor attributes."""

View file

@ -35,18 +35,10 @@ class VerisureAlarm(CoordinatorEntity, AlarmControlPanelEntity):
coordinator: VerisureDataUpdateCoordinator
_attr_name = "Verisure Alarm"
_attr_supported_features = SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY
_changed_by: str | None = None
_state: str | None = None
@property
def name(self) -> str:
"""Return the name of the entity."""
return "Verisure Alarm"
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return self.coordinator.entry.data[CONF_GIID]
@property
def device_info(self) -> DeviceInfo:
@ -58,11 +50,6 @@ class VerisureAlarm(CoordinatorEntity, AlarmControlPanelEntity):
"identifiers": {(DOMAIN, self.coordinator.entry.data[CONF_GIID])},
}
@property
def state(self) -> str | None:
"""Return the state of the entity."""
return self._state
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
@ -109,7 +96,7 @@ class VerisureAlarm(CoordinatorEntity, AlarmControlPanelEntity):
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._state = ALARM_STATE_TO_HA.get(
self._attr_state = ALARM_STATE_TO_HA.get(
self.coordinator.data["alarm"]["statusType"]
)
self._changed_by = self.coordinator.data["alarm"].get("name")

View file

@ -39,23 +39,17 @@ class VerisureDoorWindowSensor(CoordinatorEntity, BinarySensorEntity):
coordinator: VerisureDataUpdateCoordinator
_attr_device_class = DEVICE_CLASS_OPENING
def __init__(
self, coordinator: VerisureDataUpdateCoordinator, serial_number: str
) -> None:
"""Initialize the Verisure door window sensor."""
super().__init__(coordinator)
self._attr_name = coordinator.data["door_window"][serial_number]["area"]
self._attr_unique_id = f"{serial_number}_door_window"
self.serial_number = serial_number
@property
def name(self) -> str:
"""Return the name of this entity."""
return self.coordinator.data["door_window"][self.serial_number]["area"]
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return f"{self.serial_number}_door_window"
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""
@ -69,11 +63,6 @@ class VerisureDoorWindowSensor(CoordinatorEntity, BinarySensorEntity):
"via_device": (DOMAIN, self.coordinator.entry.data[CONF_GIID]),
}
@property
def device_class(self) -> str:
"""Return the class of this entity."""
return DEVICE_CLASS_OPENING
@property
def is_on(self) -> bool:
"""Return the state of the sensor."""
@ -95,10 +84,8 @@ class VerisureEthernetStatus(CoordinatorEntity, BinarySensorEntity):
coordinator: VerisureDataUpdateCoordinator
@property
def name(self) -> str:
"""Return the name of this entity."""
return "Verisure Ethernet status"
_attr_name = "Verisure Ethernet status"
_attr_device_class = DEVICE_CLASS_CONNECTIVITY
@property
def unique_id(self) -> str:
@ -124,8 +111,3 @@ class VerisureEthernetStatus(CoordinatorEntity, BinarySensorEntity):
def available(self) -> bool:
"""Return True if entity is available."""
return super().available and self.coordinator.data["ethernet"] is not None
@property
def device_class(self) -> str:
"""Return the class of this entity."""
return DEVICE_CLASS_CONNECTIVITY

View file

@ -58,21 +58,14 @@ class VerisureSmartcam(CoordinatorEntity, Camera):
super().__init__(coordinator)
Camera.__init__(self)
self._attr_name = coordinator.data["cameras"][serial_number]["area"]
self._attr_unique_id = serial_number
self.serial_number = serial_number
self._directory_path = directory_path
self._image = None
self._image_id = None
@property
def name(self) -> str:
"""Return the name of this entity."""
return self.coordinator.data["cameras"][self.serial_number]["area"]
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return self.serial_number
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""

View file

@ -65,22 +65,16 @@ class VerisureDoorlock(CoordinatorEntity, LockEntity):
) -> None:
"""Initialize the Verisure lock."""
super().__init__(coordinator)
self._attr_name = coordinator.data["locks"][serial_number]["area"]
self._attr_unique_id = serial_number
self.serial_number = serial_number
self._state = None
self._digits = coordinator.entry.options.get(
CONF_LOCK_CODE_DIGITS, DEFAULT_LOCK_CODE_DIGITS
)
@property
def name(self) -> str:
"""Return the name of this entity."""
return self.coordinator.data["locks"][self.serial_number]["area"]
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return self.serial_number
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""

View file

@ -50,11 +50,15 @@ class VerisureThermometer(CoordinatorEntity, SensorEntity):
coordinator: VerisureDataUpdateCoordinator
_attr_device_class = DEVICE_CLASS_TEMPERATURE
_attr_unit_of_measurement = TEMP_CELSIUS
def __init__(
self, coordinator: VerisureDataUpdateCoordinator, serial_number: str
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_unique_id = f"{serial_number}_temperature"
self.serial_number = serial_number
@property
@ -63,16 +67,6 @@ class VerisureThermometer(CoordinatorEntity, SensorEntity):
name = self.coordinator.data["climate"][self.serial_number]["deviceArea"]
return f"{name} Temperature"
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return f"{self.serial_number}_temperature"
@property
def device_class(self) -> str:
"""Return the class of this entity."""
return DEVICE_CLASS_TEMPERATURE
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""
@ -103,22 +97,21 @@ class VerisureThermometer(CoordinatorEntity, SensorEntity):
and "temperature" in self.coordinator.data["climate"][self.serial_number]
)
@property
def unit_of_measurement(self) -> str:
"""Return the unit of measurement of this entity."""
return TEMP_CELSIUS
class VerisureHygrometer(CoordinatorEntity, SensorEntity):
"""Representation of a Verisure hygrometer."""
coordinator: VerisureDataUpdateCoordinator
_attr_device_class = DEVICE_CLASS_HUMIDITY
_attr_unit_of_measurement = PERCENTAGE
def __init__(
self, coordinator: VerisureDataUpdateCoordinator, serial_number: str
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_unique_id = f"{serial_number}_humidity"
self.serial_number = serial_number
@property
@ -127,16 +120,6 @@ class VerisureHygrometer(CoordinatorEntity, SensorEntity):
name = self.coordinator.data["climate"][self.serial_number]["deviceArea"]
return f"{name} Humidity"
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return f"{self.serial_number}_humidity"
@property
def device_class(self) -> str:
"""Return the class of this entity."""
return DEVICE_CLASS_HUMIDITY
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""
@ -167,22 +150,20 @@ class VerisureHygrometer(CoordinatorEntity, SensorEntity):
and "humidity" in self.coordinator.data["climate"][self.serial_number]
)
@property
def unit_of_measurement(self) -> str:
"""Return the unit of measurement of this entity."""
return PERCENTAGE
class VerisureMouseDetection(CoordinatorEntity, SensorEntity):
"""Representation of a Verisure mouse detector."""
coordinator: VerisureDataUpdateCoordinator
_attr_unit_of_measurement = "Mice"
def __init__(
self, coordinator: VerisureDataUpdateCoordinator, serial_number: str
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self._attr_unique_id = f"{serial_number}_mice"
self.serial_number = serial_number
@property
@ -191,11 +172,6 @@ class VerisureMouseDetection(CoordinatorEntity, SensorEntity):
name = self.coordinator.data["mice"][self.serial_number]["area"]
return f"{name} Mouse"
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return f"{self.serial_number}_mice"
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""
@ -222,8 +198,3 @@ class VerisureMouseDetection(CoordinatorEntity, SensorEntity):
and self.serial_number in self.coordinator.data["mice"]
and "detections" in self.coordinator.data["mice"][self.serial_number]
)
@property
def unit_of_measurement(self) -> str:
"""Return the unit of measurement of this entity."""
return "Mice"

View file

@ -37,20 +37,14 @@ class VerisureSmartplug(CoordinatorEntity, SwitchEntity):
) -> None:
"""Initialize the Verisure device."""
super().__init__(coordinator)
self._attr_name = coordinator.data["smart_plugs"][serial_number]["area"]
self._attr_unique_id = serial_number
self.serial_number = serial_number
self._change_timestamp = 0
self._state = False
@property
def name(self) -> str:
"""Return the name of this entity."""
return self.coordinator.data["smart_plugs"][self.serial_number]["area"]
@property
def unique_id(self) -> str:
"""Return the unique ID for this entity."""
return self.serial_number
@property
def device_info(self) -> DeviceInfo:
"""Return device information about this entity."""

View file

@ -168,28 +168,46 @@ class Entity(ABC):
# If entity is added to an entity platform
_added = False
# Entity Properties
_attr_assumed_state: bool = False
_attr_available: bool = True
_attr_context_recent_time: timedelta = timedelta(seconds=5)
_attr_device_class: str | None = None
_attr_device_info: DeviceInfo | None = None
_attr_entity_picture: str | None = None
_attr_entity_registry_enabled_default: bool = True
_attr_extra_state_attributes: Mapping[str, Any] | None = None
_attr_force_update: bool = False
_attr_icon: str | None = None
_attr_name: str | None = None
_attr_should_poll: bool = True
_attr_state: StateType = STATE_UNKNOWN
_attr_supported_features: int | None = None
_attr_unique_id: str | None = None
_attr_unit_of_measurement: str | None = None
@property
def should_poll(self) -> bool:
"""Return True if entity has to be polled for state.
False if entity pushes its state to HA.
"""
return True
return self._attr_should_poll
@property
def unique_id(self) -> str | None:
"""Return a unique ID."""
return None
return self._attr_unique_id
@property
def name(self) -> str | None:
"""Return the name of the entity."""
return None
return self._attr_name
@property
def state(self) -> StateType:
"""Return the state of the entity."""
return STATE_UNKNOWN
return self._attr_state
@property
def capability_attributes(self) -> Mapping[str, Any] | None:
@ -227,7 +245,7 @@ class Entity(ABC):
Implemented by platform classes. Convention for attribute names
is lowercase snake_case.
"""
return None
return self._attr_extra_state_attributes
@property
def device_info(self) -> DeviceInfo | None:
@ -235,37 +253,37 @@ class Entity(ABC):
Implemented by platform classes.
"""
return None
return self._attr_device_info
@property
def device_class(self) -> str | None:
"""Return the class of this device, from component DEVICE_CLASSES."""
return None
return self._attr_device_class
@property
def unit_of_measurement(self) -> str | None:
"""Return the unit of measurement of this entity, if any."""
return None
return self._attr_unit_of_measurement
@property
def icon(self) -> str | None:
"""Return the icon to use in the frontend, if any."""
return None
return self._attr_icon
@property
def entity_picture(self) -> str | None:
"""Return the entity picture to use in the frontend, if any."""
return None
return self._attr_entity_picture
@property
def available(self) -> bool:
"""Return True if entity is available."""
return True
return self._attr_available
@property
def assumed_state(self) -> bool:
"""Return True if unable to access real state of the entity."""
return False
return self._attr_assumed_state
@property
def force_update(self) -> bool:
@ -274,22 +292,22 @@ class Entity(ABC):
If True, a state change will be triggered anytime the state property is
updated, not just when the value changes.
"""
return False
return self._attr_force_update
@property
def supported_features(self) -> int | None:
"""Flag supported features."""
return None
return self._attr_supported_features
@property
def context_recent_time(self) -> timedelta:
"""Time that a context is considered recent."""
return timedelta(seconds=5)
return self._attr_context_recent_time
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return True
return self._attr_entity_registry_enabled_default
# DO NOT OVERWRITE
# These properties and methods are either managed by Home Assistant or they