core/homeassistant/components/syncthru/sensor.py

168 lines
5.9 KiB
Python

"""Support for Samsung Printers with SyncThru web interface."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, cast
from pysyncthru import SyncThru, SyncthruState
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import SyncThruConfigEntry
from .entity import SyncthruEntity
SYNCTHRU_STATE_HUMAN = {
SyncthruState.INVALID: "invalid",
SyncthruState.OFFLINE: "unreachable",
SyncthruState.NORMAL: "normal",
SyncthruState.UNKNOWN: "unknown",
SyncthruState.WARNING: "warning",
SyncthruState.TESTING: "testing",
SyncthruState.ERROR: "error",
}
@dataclass(frozen=True, kw_only=True)
class SyncThruSensorDescription(SensorEntityDescription):
"""Describes a SyncThru sensor entity."""
value_fn: Callable[[SyncThru], str | None]
extra_state_attributes_fn: Callable[[SyncThru], dict[str, str | int]] | None = None
def get_toner_entity_description(color: str) -> SyncThruSensorDescription:
"""Get toner entity description for a specific color."""
return SyncThruSensorDescription(
key=f"toner_{color}",
translation_key=f"toner_{color}",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda printer: printer.toner_status().get(color, {}).get("remaining"),
extra_state_attributes_fn=lambda printer: printer.toner_status().get(color, {}),
)
def get_drum_entity_description(color: str) -> SyncThruSensorDescription:
"""Get drum entity description for a specific color."""
return SyncThruSensorDescription(
key=f"drum_{color}",
translation_key=f"drum_{color}",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
value_fn=lambda printer: printer.drum_status().get(color, {}).get("remaining"),
extra_state_attributes_fn=lambda printer: printer.drum_status().get(color, {}),
)
def get_input_tray_entity_description(tray: str) -> SyncThruSensorDescription:
"""Get input tray entity description for a specific tray."""
placeholders = {}
translation_key = f"tray_{tray}"
if "_" in tray:
_, identifier = tray.split("_")
placeholders["tray_number"] = identifier
translation_key = "tray"
return SyncThruSensorDescription(
key=f"tray_{tray}",
translation_key=translation_key,
entity_category=EntityCategory.DIAGNOSTIC,
translation_placeholders=placeholders,
value_fn=(
lambda printer: printer.input_tray_status().get(tray, {}).get("newError")
or "Ready"
),
extra_state_attributes_fn=(
lambda printer: printer.input_tray_status().get(tray, {})
),
)
def get_output_tray_entity_description(tray: int) -> SyncThruSensorDescription:
"""Get output tray entity description for a specific tray."""
return SyncThruSensorDescription(
key=f"output_tray_{tray}",
translation_key="output_tray",
entity_category=EntityCategory.DIAGNOSTIC,
translation_placeholders={"tray_number": str(tray)},
value_fn=(
lambda printer: printer.output_tray_status().get(tray, {}).get("status")
or "Ready"
),
extra_state_attributes_fn=(
lambda printer: cast(
dict[str, str | int], printer.output_tray_status().get(tray, {})
)
),
)
SENSOR_TYPES: tuple[SyncThruSensorDescription, ...] = (
SyncThruSensorDescription(
key="active_alerts",
translation_key="active_alerts",
value_fn=lambda printer: printer.raw().get("GXI_ACTIVE_ALERT_TOTAL"),
),
SyncThruSensorDescription(
key="main",
name=None,
value_fn=lambda printer: SYNCTHRU_STATE_HUMAN[printer.device_status()],
extra_state_attributes_fn=lambda printer: {
"display_text": printer.device_status_details(),
},
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: SyncThruConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up from config entry."""
coordinator = config_entry.runtime_data
printer = coordinator.data
supp_toner = printer.toner_status(filter_supported=True)
supp_drum = printer.drum_status(filter_supported=True)
supp_tray = printer.input_tray_status(filter_supported=True)
supp_output_tray = printer.output_tray_status()
entities: list[SyncThruSensorDescription] = [
get_toner_entity_description(color) for color in supp_toner
]
entities.extend(get_drum_entity_description(color) for color in supp_drum)
entities.extend(get_input_tray_entity_description(key) for key in supp_tray)
entities.extend(get_output_tray_entity_description(key) for key in supp_output_tray)
async_add_entities(
SyncThruSensor(coordinator, description)
for description in SENSOR_TYPES + tuple(entities)
)
class SyncThruSensor(SyncthruEntity, SensorEntity):
"""Implementation of an abstract Samsung Printer sensor platform."""
_attr_icon = "mdi:printer"
entity_description: SyncThruSensorDescription
@property
def native_value(self) -> str | int | None:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data)
@property
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
if self.entity_description.extra_state_attributes_fn:
return self.entity_description.extra_state_attributes_fn(
self.coordinator.data
)
return None