Add sensor entity descriptions in Minecraft Server (#99971)

* Add sensor entity descriptions

* Fix review findings

* Fix type of value function to avoid inline lambda if conditions and add attribute function to avoid extra sensor entity class

* Correct name of binary sensor base entity

* Simplify adding of entities in platforms

* Do not use keyword arguments while adding entities
This commit is contained in:
elmurato 2023-09-10 10:20:26 +02:00 committed by GitHub
parent 4153181cd3
commit 1f3b3b1be3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 146 additions and 158 deletions

View File

@ -1,7 +1,10 @@
"""The Minecraft Server binary sensor platform.""" """The Minecraft Server binary sensor platform."""
from dataclasses import dataclass
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -12,6 +15,21 @@ from .const import DOMAIN, ICON_STATUS, KEY_STATUS
from .entity import MinecraftServerEntity from .entity import MinecraftServerEntity
@dataclass
class MinecraftServerBinarySensorEntityDescription(BinarySensorEntityDescription):
"""Class describing Minecraft Server binary sensor entities."""
BINARY_SENSOR_DESCRIPTIONS = [
MinecraftServerBinarySensorEntityDescription(
key=KEY_STATUS,
translation_key=KEY_STATUS,
device_class=BinarySensorDeviceClass.CONNECTIVITY,
icon=ICON_STATUS,
),
]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ConfigEntry,
@ -20,28 +38,32 @@ async def async_setup_entry(
"""Set up the Minecraft Server binary sensor platform.""" """Set up the Minecraft Server binary sensor platform."""
server = hass.data[DOMAIN][config_entry.entry_id] server = hass.data[DOMAIN][config_entry.entry_id]
# Create entities list.
entities = [MinecraftServerStatusBinarySensor(server)]
# Add binary sensor entities. # Add binary sensor entities.
async_add_entities(entities, True) async_add_entities(
[
MinecraftServerBinarySensorEntity(server, description)
for description in BINARY_SENSOR_DESCRIPTIONS
],
True,
)
class MinecraftServerStatusBinarySensor(MinecraftServerEntity, BinarySensorEntity): class MinecraftServerBinarySensorEntity(MinecraftServerEntity, BinarySensorEntity):
"""Representation of a Minecraft Server status binary sensor.""" """Representation of a Minecraft Server binary sensor base entity."""
_attr_translation_key = KEY_STATUS entity_description: MinecraftServerBinarySensorEntityDescription
def __init__(self, server: MinecraftServer) -> None: def __init__(
"""Initialize status binary sensor.""" self,
super().__init__( server: MinecraftServer,
server=server, description: MinecraftServerBinarySensorEntityDescription,
entity_type=KEY_STATUS, ) -> None:
icon=ICON_STATUS, """Initialize binary sensor base entity."""
device_class=BinarySensorDeviceClass.CONNECTIVITY, super().__init__(server=server)
) self.entity_description = description
self._attr_unique_id = f"{server.unique_id}-{description.key}"
self._attr_is_on = False self._attr_is_on = False
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update status.""" """Update binary sensor state."""
self._attr_is_on = self._server.online self._attr_is_on = self._server.online

View File

@ -19,23 +19,16 @@ class MinecraftServerEntity(Entity):
def __init__( def __init__(
self, self,
server: MinecraftServer, server: MinecraftServer,
entity_type: str,
icon: str,
device_class: str | None,
) -> None: ) -> None:
"""Initialize base entity.""" """Initialize base entity."""
self._server = server self._server = server
self._attr_icon = icon
self._attr_unique_id = f"{self._server.unique_id}-{entity_type}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._server.unique_id)}, identifiers={(DOMAIN, server.unique_id)},
manufacturer=MANUFACTURER, manufacturer=MANUFACTURER,
model=f"Minecraft Server ({self._server.data.version})", model=f"Minecraft Server ({server.data.version})",
name=self._server.name, name=server.name,
sw_version=f"{self._server.data.protocol_version}", sw_version=str(server.data.protocol_version),
) )
self._attr_device_class = device_class
self._extra_state_attributes = None
self._disconnect_dispatcher: CALLBACK_TYPE | None = None self._disconnect_dispatcher: CALLBACK_TYPE | None = None
async def async_update(self) -> None: async def async_update(self) -> None:

View File

@ -1,13 +1,18 @@
"""The Minecraft Server sensor platform.""" """The Minecraft Server sensor platform."""
from __future__ import annotations from __future__ import annotations
from homeassistant.components.sensor import SensorEntity from collections.abc import Callable, MutableMapping
from dataclasses import dataclass
from typing import Any
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTime from homeassistant.const import UnitOfTime
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from . import MinecraftServer from . import MinecraftServer, MinecraftServerData
from .const import ( from .const import (
ATTR_PLAYERS_LIST, ATTR_PLAYERS_LIST,
DOMAIN, DOMAIN,
@ -29,6 +34,84 @@ from .const import (
from .entity import MinecraftServerEntity from .entity import MinecraftServerEntity
@dataclass
class MinecraftServerEntityDescriptionMixin:
"""Mixin values for Minecraft Server entities."""
value_fn: Callable[[MinecraftServerData], StateType]
attributes_fn: Callable[[MinecraftServerData], MutableMapping[str, Any]] | None
@dataclass
class MinecraftServerSensorEntityDescription(
SensorEntityDescription, MinecraftServerEntityDescriptionMixin
):
"""Class describing Minecraft Server sensor entities."""
def get_extra_state_attributes_players_list(
data: MinecraftServerData,
) -> dict[str, list[str]]:
"""Return players list as extra state attributes, if available."""
extra_state_attributes = {}
players_list = data.players_list
if players_list is not None and len(players_list) != 0:
extra_state_attributes[ATTR_PLAYERS_LIST] = players_list
return extra_state_attributes
SENSOR_DESCRIPTIONS = [
MinecraftServerSensorEntityDescription(
key=KEY_VERSION,
translation_key=KEY_VERSION,
icon=ICON_VERSION,
value_fn=lambda data: data.version,
attributes_fn=None,
),
MinecraftServerSensorEntityDescription(
key=KEY_PROTOCOL_VERSION,
translation_key=KEY_PROTOCOL_VERSION,
icon=ICON_PROTOCOL_VERSION,
value_fn=lambda data: data.protocol_version,
attributes_fn=None,
),
MinecraftServerSensorEntityDescription(
key=KEY_PLAYERS_MAX,
translation_key=KEY_PLAYERS_MAX,
native_unit_of_measurement=UNIT_PLAYERS_MAX,
icon=ICON_PLAYERS_MAX,
value_fn=lambda data: data.players_max,
attributes_fn=None,
),
MinecraftServerSensorEntityDescription(
key=KEY_LATENCY,
translation_key=KEY_LATENCY,
native_unit_of_measurement=UnitOfTime.MILLISECONDS,
suggested_display_precision=0,
icon=ICON_LATENCY,
value_fn=lambda data: data.latency,
attributes_fn=None,
),
MinecraftServerSensorEntityDescription(
key=KEY_MOTD,
translation_key=KEY_MOTD,
icon=ICON_MOTD,
value_fn=lambda data: data.motd,
attributes_fn=None,
),
MinecraftServerSensorEntityDescription(
key=KEY_PLAYERS_ONLINE,
translation_key=KEY_PLAYERS_ONLINE,
native_unit_of_measurement=UNIT_PLAYERS_ONLINE,
icon=ICON_PLAYERS_ONLINE,
value_fn=lambda data: data.players_online,
attributes_fn=get_extra_state_attributes_players_list,
),
]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: ConfigEntry,
@ -37,151 +120,41 @@ async def async_setup_entry(
"""Set up the Minecraft Server sensor platform.""" """Set up the Minecraft Server sensor platform."""
server = hass.data[DOMAIN][config_entry.entry_id] server = hass.data[DOMAIN][config_entry.entry_id]
# Create entities list.
entities = [
MinecraftServerVersionSensor(server),
MinecraftServerProtocolVersionSensor(server),
MinecraftServerLatencySensor(server),
MinecraftServerPlayersOnlineSensor(server),
MinecraftServerPlayersMaxSensor(server),
MinecraftServerMOTDSensor(server),
]
# Add sensor entities. # Add sensor entities.
async_add_entities(entities, True) async_add_entities(
[
MinecraftServerSensorEntity(server, description)
for description in SENSOR_DESCRIPTIONS
],
True,
)
class MinecraftServerSensorEntity(MinecraftServerEntity, SensorEntity): class MinecraftServerSensorEntity(MinecraftServerEntity, SensorEntity):
"""Representation of a Minecraft Server sensor base entity.""" """Representation of a Minecraft Server sensor base entity."""
entity_description: MinecraftServerSensorEntityDescription
def __init__( def __init__(
self, self,
server: MinecraftServer, server: MinecraftServer,
entity_type: str, description: MinecraftServerSensorEntityDescription,
icon: str,
unit: str | None = None,
device_class: str | None = None,
) -> None: ) -> None:
"""Initialize sensor base entity.""" """Initialize sensor base entity."""
super().__init__(server, entity_type, icon, device_class) super().__init__(server)
self._attr_native_unit_of_measurement = unit self.entity_description = description
self._attr_unique_id = f"{server.unique_id}-{description.key}"
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return sensor availability.""" """Return sensor availability."""
return self._server.online return self._server.online
class MinecraftServerVersionSensor(MinecraftServerSensorEntity):
"""Representation of a Minecraft Server version sensor."""
_attr_translation_key = KEY_VERSION
def __init__(self, server: MinecraftServer) -> None:
"""Initialize version sensor."""
super().__init__(server=server, entity_type=KEY_VERSION, icon=ICON_VERSION)
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update version.""" """Update sensor state."""
self._attr_native_value = self._server.data.version self._attr_native_value = self.entity_description.value_fn(self._server.data)
if self.entity_description.attributes_fn:
class MinecraftServerProtocolVersionSensor(MinecraftServerSensorEntity): self._attr_extra_state_attributes = self.entity_description.attributes_fn(
"""Representation of a Minecraft Server protocol version sensor.""" self._server.data
)
_attr_translation_key = KEY_PROTOCOL_VERSION
def __init__(self, server: MinecraftServer) -> None:
"""Initialize protocol version sensor."""
super().__init__(
server=server,
entity_type=KEY_PROTOCOL_VERSION,
icon=ICON_PROTOCOL_VERSION,
)
async def async_update(self) -> None:
"""Update protocol version."""
self._attr_native_value = self._server.data.protocol_version
class MinecraftServerLatencySensor(MinecraftServerSensorEntity):
"""Representation of a Minecraft Server latency sensor."""
_attr_translation_key = KEY_LATENCY
def __init__(self, server: MinecraftServer) -> None:
"""Initialize latency sensor."""
super().__init__(
server=server,
entity_type=KEY_LATENCY,
icon=ICON_LATENCY,
unit=UnitOfTime.MILLISECONDS,
)
async def async_update(self) -> None:
"""Update latency."""
self._attr_native_value = self._server.data.latency
class MinecraftServerPlayersOnlineSensor(MinecraftServerSensorEntity):
"""Representation of a Minecraft Server online players sensor."""
_attr_translation_key = KEY_PLAYERS_ONLINE
def __init__(self, server: MinecraftServer) -> None:
"""Initialize online players sensor."""
super().__init__(
server=server,
entity_type=KEY_PLAYERS_ONLINE,
icon=ICON_PLAYERS_ONLINE,
unit=UNIT_PLAYERS_ONLINE,
)
async def async_update(self) -> None:
"""Update online players state and device state attributes."""
self._attr_native_value = self._server.data.players_online
extra_state_attributes = {}
players_list = self._server.data.players_list
if players_list is not None and len(players_list) != 0:
extra_state_attributes[ATTR_PLAYERS_LIST] = players_list
self._attr_extra_state_attributes = extra_state_attributes
class MinecraftServerPlayersMaxSensor(MinecraftServerSensorEntity):
"""Representation of a Minecraft Server maximum number of players sensor."""
_attr_translation_key = KEY_PLAYERS_MAX
def __init__(self, server: MinecraftServer) -> None:
"""Initialize maximum number of players sensor."""
super().__init__(
server=server,
entity_type=KEY_PLAYERS_MAX,
icon=ICON_PLAYERS_MAX,
unit=UNIT_PLAYERS_MAX,
)
async def async_update(self) -> None:
"""Update maximum number of players."""
self._attr_native_value = self._server.data.players_max
class MinecraftServerMOTDSensor(MinecraftServerSensorEntity):
"""Representation of a Minecraft Server MOTD sensor."""
_attr_translation_key = KEY_MOTD
def __init__(self, server: MinecraftServer) -> None:
"""Initialize MOTD sensor."""
super().__init__(
server=server,
entity_type=KEY_MOTD,
icon=ICON_MOTD,
)
async def async_update(self) -> None:
"""Update MOTD."""
self._attr_native_value = self._server.data.motd