Files

185 lines
6.0 KiB
Python

"""Sensor for Steam account status."""
from collections.abc import Callable, Mapping
from dataclasses import dataclass
from datetime import datetime
from enum import StrEnum
from typing import TYPE_CHECKING, Any, override
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util import dt as dt_util
from .const import (
STEAM_API_URL,
STEAM_HEADER_IMAGE_FILE,
STEAM_ICON_URL,
STEAM_MAIN_IMAGE_FILE,
STEAM_STATUSES,
SUBENTRY_TYPE_FRIEND,
)
from .coordinator import PlayerData, SteamConfigEntry
from .entity import SteamEntity
PARALLEL_UPDATES = 1
class SteamSensor(StrEnum):
"""Steam sensors."""
ACCOUNT = "account"
LAST_ONLINE = "last_online"
NOW_PLAYING = "now_playing"
LEVEL = "level"
@dataclass(kw_only=True, frozen=True)
class SteamSensorEntityDescription(SensorEntityDescription):
"""Steam sensor description."""
value_fn: Callable[[PlayerData], StateType | datetime]
entity_picture_fn: Callable[[PlayerData, dict[str, str]], str | None] | None = None
extra_state_attributes_fn: (
Callable[[PlayerData, dict[str, str]], Mapping[str, Any]] | None
) = None
SENSOR_DESCRIPTIONS: tuple[SteamSensorEntityDescription, ...] = (
SteamSensorEntityDescription(
key=SteamSensor.ACCOUNT,
translation_key=SteamSensor.ACCOUNT,
value_fn=lambda x: STEAM_STATUSES[x.personastate],
device_class=SensorDeviceClass.ENUM,
options=list(STEAM_STATUSES.values()),
entity_picture_fn=lambda x, _: x.avatarfull,
name=None,
# Attributes game, game_id, game_image_header, game_image_main, game_icon,
# last_online, and level are deprecated and can be removed in 2027.2
extra_state_attributes_fn=lambda x, icons: {
"real_name": x.realname,
"created": (
dt_util.as_local(dt_util.utc_from_timestamp(x.timecreated))
if x.timecreated is not None
else None
),
"game": x.gameextrainfo,
"game_id": x.gameid,
"game_image_header": (
f"{STEAM_API_URL}{x.gameid}/{STEAM_HEADER_IMAGE_FILE}"
if x.gameid is not None
else None
),
"game_image_main": (
f"{STEAM_API_URL}{x.gameid}/{STEAM_MAIN_IMAGE_FILE}"
if x.gameid is not None
else None
),
"game_icon": (
f"{STEAM_ICON_URL}{x.gameid}/{info}.jpg"
if x.gameid is not None and (info := icons.get(x.gameid)) is not None
else None
),
"last_online": (
dt_util.utc_from_timestamp(x.lastlogoff)
if x.lastlogoff is not None
else None
),
"level": x.level,
},
),
SteamSensorEntityDescription(
key=SteamSensor.LAST_ONLINE,
translation_key=SteamSensor.LAST_ONLINE,
value_fn=(
lambda x: (
dt_util.utc_from_timestamp(x.lastlogoff)
if x.lastlogoff is not None
else None
)
),
device_class=SensorDeviceClass.TIMESTAMP,
),
SteamSensorEntityDescription(
key=SteamSensor.NOW_PLAYING,
translation_key=SteamSensor.NOW_PLAYING,
value_fn=lambda x: x.gameextrainfo,
entity_picture_fn=lambda x, icons: (
f"{STEAM_ICON_URL}{x.gameid}/{game_icon_url}.jpg"
if x.gameid and (game_icon_url := icons.get(x.gameid))
else None
),
extra_state_attributes_fn=lambda x, _: {"app_id": x.gameid},
),
SteamSensorEntityDescription(
key=SteamSensor.LEVEL,
translation_key=SteamSensor.LEVEL,
value_fn=lambda x: x.level,
state_class=SensorStateClass.MEASUREMENT,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: SteamConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Steam platform."""
coordinator = entry.runtime_data
if TYPE_CHECKING:
assert entry.unique_id
async_add_entities(
SteamSensorEntity(coordinator, entry.unique_id, description)
for description in SENSOR_DESCRIPTIONS
if entry.unique_id in coordinator.data
)
for subentry in entry.get_subentries_of_type(SUBENTRY_TYPE_FRIEND):
async_add_entities(
[
SteamSensorEntity(coordinator, subentry.unique_id, description)
for description in SENSOR_DESCRIPTIONS
if subentry.unique_id in coordinator.data
],
config_subentry_id=subentry.subentry_id,
)
class SteamSensorEntity(SteamEntity, SensorEntity):
"""Representation of a Steam sensor entity."""
entity_description: SteamSensorEntityDescription
@property
@override
def native_value(self) -> StateType | datetime:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data[self._steamid])
@property
@override
def entity_picture(self) -> str | None:
"""Return the entity picture to use in the frontend, if any."""
return (
fn(self.coordinator.data[self._steamid], self.coordinator.game_icons)
if (fn := self.entity_description.entity_picture_fn) is not None
else super().entity_picture
)
@property
@override
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return the state attributes of the sensor."""
return (
fn(self.coordinator.data[self._steamid], self.coordinator.game_icons)
if (fn := self.entity_description.extra_state_attributes_fn) is not None
else super().extra_state_attributes
)