Add Sensor for Refoss Integration (#116965)

Co-authored-by: Robert Resch <robert@resch.dev>
pull/120016/head^2
ashionky 2024-06-20 16:29:37 +08:00 committed by GitHub
parent 1eb8b5a27c
commit 3224224bf8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 219 additions and 5 deletions

View File

@ -1110,6 +1110,7 @@ omit =
homeassistant/components/refoss/bridge.py
homeassistant/components/refoss/coordinator.py
homeassistant/components/refoss/entity.py
homeassistant/components/refoss/sensor.py
homeassistant/components/refoss/switch.py
homeassistant/components/refoss/util.py
homeassistant/components/rejseplanen/sensor.py

View File

@ -15,6 +15,7 @@ from .const import COORDINATORS, DATA_DISCOVERY_SERVICE, DISCOVERY_SCAN_INTERVAL
from .util import refoss_discovery_server
PLATFORMS: Final = [
Platform.SENSOR,
Platform.SWITCH,
]

View File

@ -19,3 +19,14 @@ DOMAIN = "refoss"
COORDINATOR = "coordinator"
MAX_ERRORS = 2
CHANNEL_DISPLAY_NAME: dict[str, dict[int, str]] = {
"em06": {
1: "A1",
2: "B1",
3: "C1",
4: "A2",
5: "B2",
6: "C2",
}
}

View File

@ -18,11 +18,6 @@ class RefossEntity(CoordinatorEntity[RefossDataUpdateCoordinator]):
mac = coordinator.device.mac
self.channel_id = channel
if channel == 0:
self._attr_name = None
else:
self._attr_name = str(channel)
self._attr_unique_id = f"{mac}_{channel}"
self._attr_device_info = DeviceInfo(
connections={(CONNECTION_NETWORK_MAC, mac)},

View File

@ -0,0 +1,174 @@
"""Support for refoss sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from refoss_ha.controller.electricity import ElectricityXMix
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from .bridge import RefossDataUpdateCoordinator
from .const import (
CHANNEL_DISPLAY_NAME,
COORDINATORS,
DISPATCH_DEVICE_DISCOVERED,
DOMAIN,
)
from .entity import RefossEntity
@dataclass(frozen=True)
class RefossSensorEntityDescription(SensorEntityDescription):
"""Describes Refoss sensor entity."""
subkey: str | None = None
fn: Callable[[float], float] | None = None
SENSORS: dict[str, tuple[RefossSensorEntityDescription, ...]] = {
"em06": (
RefossSensorEntityDescription(
key="power",
translation_key="power",
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_display_precision=2,
subkey="power",
fn=lambda x: x / 1000.0,
),
RefossSensorEntityDescription(
key="voltage",
translation_key="voltage",
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
suggested_display_precision=2,
suggested_unit_of_measurement=UnitOfElectricPotential.VOLT,
subkey="voltage",
),
RefossSensorEntityDescription(
key="current",
translation_key="current",
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.MILLIAMPERE,
suggested_display_precision=2,
suggested_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
subkey="current",
),
RefossSensorEntityDescription(
key="factor",
translation_key="power_factor",
device_class=SensorDeviceClass.POWER_FACTOR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=2,
subkey="factor",
),
RefossSensorEntityDescription(
key="energy",
translation_key="this_month_energy",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_display_precision=2,
subkey="mConsume",
fn=lambda x: x if x > 0 else 0,
),
RefossSensorEntityDescription(
key="energy_returned",
translation_key="this_month_energy_returned",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_display_precision=2,
subkey="mConsume",
fn=lambda x: abs(x) if x < 0 else 0,
),
),
}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Refoss device from a config entry."""
@callback
def init_device(coordinator):
"""Register the device."""
device = coordinator.device
if not isinstance(device, ElectricityXMix):
return
descriptions = SENSORS.get(device.device_type)
new_entities = []
for channel in device.channels:
for description in descriptions:
entity = RefossSensor(
coordinator=coordinator,
channel=channel,
description=description,
)
new_entities.append(entity)
async_add_entities(new_entities)
for coordinator in hass.data[DOMAIN][COORDINATORS]:
init_device(coordinator)
config_entry.async_on_unload(
async_dispatcher_connect(hass, DISPATCH_DEVICE_DISCOVERED, init_device)
)
class RefossSensor(RefossEntity, SensorEntity):
"""Refoss Sensor Device."""
entity_description: RefossSensorEntityDescription
def __init__(
self,
coordinator: RefossDataUpdateCoordinator,
channel: int,
description: RefossSensorEntityDescription,
) -> None:
"""Init Refoss sensor."""
super().__init__(coordinator, channel)
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
device_type = coordinator.device.device_type
channel_name = CHANNEL_DISPLAY_NAME[device_type][channel]
self._attr_translation_placeholders = {"channel_name": channel_name}
@property
def native_value(self) -> StateType:
"""Return the native value."""
value = self.coordinator.device.get_value(
self.channel_id, self.entity_description.subkey
)
if value is None:
return None
if self.entity_description.fn is not None:
return self.entity_description.fn(value)
return value

View File

@ -9,5 +9,27 @@
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
}
},
"entity": {
"sensor": {
"power": {
"name": "{channel_name} power"
},
"voltage": {
"name": "{channel_name} voltage"
},
"current": {
"name": "{channel_name} current"
},
"power_factor": {
"name": "{channel_name} power factor"
},
"this_month_energy": {
"name": "{channel_name} this month energy"
},
"this_month_energy_returned": {
"name": "{channel_name} this month energy returned"
}
}
}
}

View File

@ -12,6 +12,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .bridge import RefossDataUpdateCoordinator
from .const import COORDINATORS, DISPATCH_DEVICE_DISCOVERED, DOMAIN
from .entity import RefossEntity
@ -48,6 +49,15 @@ async def async_setup_entry(
class RefossSwitch(RefossEntity, SwitchEntity):
"""Refoss Switch Device."""
def __init__(
self,
coordinator: RefossDataUpdateCoordinator,
channel: int,
) -> None:
"""Init Refoss switch."""
super().__init__(coordinator, channel)
self._attr_name = str(channel)
@property
def is_on(self) -> bool | None:
"""Return true if switch is on."""