Bump iammeter to 0.2.1 (#95885)

* Bump iammeter to 0.2.1

* Refactor sensor.

* Add const.py to .coveragerc.

* Add id migration.

* Modify translation file.

* Fix ruff test error

* update asyncio.timeout import.

* Delete homeassistant/components/iammeter/translations directory

* Add strings.json
This commit is contained in:
yangbo 2023-12-14 17:01:29 +08:00 committed by GitHub
parent 0ec3a222e3
commit 82f0b28e89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 486 additions and 53 deletions

View File

@ -539,6 +539,7 @@ omit =
homeassistant/components/hvv_departures/binary_sensor.py
homeassistant/components/hvv_departures/sensor.py
homeassistant/components/ialarm/alarm_control_panel.py
homeassistant/components/iammeter/const.py
homeassistant/components/iammeter/sensor.py
homeassistant/components/iaqualink/binary_sensor.py
homeassistant/components/iaqualink/climate.py

View File

@ -1 +1 @@
"""Support for IamMeter Devices."""
"""Iammeter integration."""

View File

@ -0,0 +1,11 @@
"""Constants for the Iammeter integration."""
from __future__ import annotations
DOMAIN = "iammeter"
# Default config for iammeter.
DEFAULT_IP = "192.168.2.15"
DEFAULT_NAME = "IamMeter"
DEVICE_3080 = "WEM3080"
DEVICE_3080T = "WEM3080T"
DEVICE_TYPES = [DEVICE_3080, DEVICE_3080T]

View File

@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/iammeter",
"iot_class": "local_polling",
"loggers": ["iammeter"],
"requirements": ["iammeter==0.1.7"]
"requirements": ["iammeter==0.2.1"]
}

View File

@ -2,26 +2,44 @@
from __future__ import annotations
import asyncio
from datetime import timedelta
from asyncio import timeout
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
import logging
from iammeter import real_time_api
from iammeter.power_meter import IamMeterError
from iammeter.client import IamMeter
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT
from homeassistant.components.sensor import (
PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
CONF_PORT,
PERCENTAGE,
Platform,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfFrequency,
UnitOfPower,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import debounce
from homeassistant.helpers import debounce, entity_registry as er, update_coordinator
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DEVICE_3080, DOMAIN
_LOGGER = logging.getLogger(__name__)
@ -40,6 +58,51 @@ SCAN_INTERVAL = timedelta(seconds=30)
PLATFORM_TIMEOUT = 8
def _migrate_to_new_unique_id(
hass: HomeAssistant, model: str, serial_number: str
) -> None:
"""Migrate old unique ids to new unique ids."""
ent_reg = er.async_get(hass)
name_list = [
"Voltage",
"Current",
"Power",
"ImportEnergy",
"ExportGrid",
"Frequency",
"PF",
]
phase_list = ["A", "B", "C", "NET"]
id_phase_range = 1 if model == DEVICE_3080 else 4
id_name_range = 5 if model == DEVICE_3080 else 7
for row in range(0, id_phase_range):
for idx in range(0, id_name_range):
old_unique_id = f"{serial_number}-{row}-{idx}"
new_unique_id = (
f"{serial_number}_{name_list[idx]}"
if model == DEVICE_3080
else f"{serial_number}_{name_list[idx]}_{phase_list[row]}"
)
entity_id = ent_reg.async_get_entity_id(
Platform.SENSOR, DOMAIN, old_unique_id
)
if entity_id is not None:
try:
ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
except ValueError:
_LOGGER.warning(
"Skip migration of id [%s] to [%s] because it already exists",
old_unique_id,
new_unique_id,
)
else:
_LOGGER.debug(
"Migrating unique_id from [%s] to [%s]",
old_unique_id,
new_unique_id,
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -51,23 +114,24 @@ async def async_setup_platform(
config_port = config[CONF_PORT]
config_name = config[CONF_NAME]
try:
async with asyncio.timeout(PLATFORM_TIMEOUT):
api = await real_time_api(config_host, config_port)
except (IamMeterError, asyncio.TimeoutError) as err:
api = await hass.async_add_executor_job(
IamMeter, config_host, config_port, config_name
)
except asyncio.TimeoutError as err:
_LOGGER.error("Device is not ready")
raise PlatformNotReady from err
async def async_update_data():
try:
async with asyncio.timeout(PLATFORM_TIMEOUT):
return await api.get_data()
except (IamMeterError, asyncio.TimeoutError) as err:
async with timeout(PLATFORM_TIMEOUT):
return await hass.async_add_executor_job(api.client.get_data)
except asyncio.TimeoutError as err:
raise UpdateFailed from err
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name=DEFAULT_DEVICE_NAME,
name=config_name,
update_method=async_update_data,
update_interval=SCAN_INTERVAL,
request_refresh_debouncer=debounce.Debouncer(
@ -75,46 +139,334 @@ async def async_setup_platform(
),
)
await coordinator.async_refresh()
entities = []
for sensor_name, (row, idx, unit) in api.iammeter.sensor_map().items():
serial_number = api.iammeter.serial_number
uid = f"{serial_number}-{row}-{idx}"
entities.append(IamMeter(coordinator, uid, sensor_name, unit, config_name))
async_add_entities(entities)
model = coordinator.data["Model"]
serial_number = coordinator.data["sn"]
_migrate_to_new_unique_id(hass, model, serial_number)
if model == DEVICE_3080:
async_add_entities(
IammeterSensor(coordinator, description)
for description in SENSOR_TYPES_3080
)
else: # DEVICE_3080T:
async_add_entities(
IammeterSensor(coordinator, description)
for description in SENSOR_TYPES_3080T
)
class IamMeter(CoordinatorEntity, SensorEntity):
"""Class for a sensor."""
class IammeterSensor(update_coordinator.CoordinatorEntity, SensorEntity):
"""Representation of a Sensor."""
def __init__(self, coordinator, uid, sensor_name, unit, dev_name):
"""Initialize an iammeter sensor."""
entity_description: IammeterSensorEntityDescription
_attr_has_entity_name = True
_attr_name = None
def __init__(
self,
coordinator: DataUpdateCoordinator,
description: IammeterSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.uid = uid
self.sensor_name = sensor_name
self.unit = unit
self.dev_name = dev_name
self.entity_description = description
self._attr_unique_id = f"{coordinator.data['sn']}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.data["sn"])},
manufacturer="IamMeter",
name=coordinator.name,
)
@property
def native_value(self):
"""Return the state of the sensor."""
return self.coordinator.data.data[self.sensor_name]
"""Return the native sensor value."""
raw_attr = self.coordinator.data.get(self.entity_description.key, None)
if self.entity_description.value:
return self.entity_description.value(raw_attr)
return raw_attr
@property
def unique_id(self):
"""Return unique id."""
return self.uid
@property
def name(self):
"""Name of this iammeter attribute."""
return f"{self.dev_name} {self.sensor_name}"
@dataclass
class IammeterSensorEntityDescription(SensorEntityDescription):
"""Describes Iammeter sensor entity."""
@property
def icon(self):
"""Icon for each sensor."""
return "mdi:flash"
value: Callable[[float | int], float] | Callable[[datetime], datetime] | None = None
@property
def native_unit_of_measurement(self):
"""Return the unit of measurement."""
return self.unit
SENSOR_TYPES_3080: tuple[IammeterSensorEntityDescription, ...] = (
IammeterSensorEntityDescription(
key="Voltage",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Current",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.CURRENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Power",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
IammeterSensorEntityDescription(
key="ImportEnergy",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="ExportGrid",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
)
SENSOR_TYPES_3080T: tuple[IammeterSensorEntityDescription, ...] = (
IammeterSensorEntityDescription(
key="Voltage_A",
translation_key="voltage_a",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Current_A",
translation_key="current_a",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.CURRENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Power_A",
translation_key="power_a",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
IammeterSensorEntityDescription(
key="ImportEnergy_A",
translation_key="import_energy_a",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="ExportGrid_A",
translation_key="export_grid_a",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="Frequency_A",
translation_key="frequency_a",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfFrequency.HERTZ,
device_class=SensorDeviceClass.FREQUENCY,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="PF_A",
translation_key="pf_a",
icon="mdi:solar-power",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER_FACTOR,
value=lambda value: value * 100,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Voltage_B",
translation_key="voltage_b",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Current_B",
translation_key="current_b",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.CURRENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Power_B",
translation_key="power_b",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
IammeterSensorEntityDescription(
key="ImportEnergy_B",
translation_key="import_energy_b",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="ExportGrid_B",
translation_key="export_grid_b",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="Frequency_B",
translation_key="frequency_b",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfFrequency.HERTZ,
device_class=SensorDeviceClass.FREQUENCY,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="PF_B",
translation_key="pf_b",
icon="mdi:solar-power",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER_FACTOR,
value=lambda value: value * 100,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Voltage_C",
translation_key="voltage_c",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Current_C",
translation_key="current_c",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.CURRENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Power_C",
translation_key="power_c",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
),
IammeterSensorEntityDescription(
key="ImportEnergy_C",
translation_key="import_energy_c",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="ExportGrid_C",
translation_key="export_grid_c",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
),
IammeterSensorEntityDescription(
key="Frequency_C",
translation_key="frequency_c",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfFrequency.HERTZ,
device_class=SensorDeviceClass.FREQUENCY,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="PF_C",
translation_key="pf_c",
icon="mdi:solar-power",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER_FACTOR,
value=lambda value: value * 100,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Voltage_Net",
translation_key="voltage_net",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Power_Net",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="ImportEnergy_Net",
translation_key="import_energy_net",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="ExportGrid_Net",
translation_key="export_grid_net",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="Frequency_Net",
translation_key="frequency_net",
icon="mdi:solar-power",
native_unit_of_measurement=UnitOfFrequency.HERTZ,
device_class=SensorDeviceClass.FREQUENCY,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
),
IammeterSensorEntityDescription(
key="PF_Net",
translation_key="pf_net",
icon="mdi:solar-power",
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER_FACTOR,
value=lambda value: value * 100,
entity_registry_enabled_default=False,
),
)

View File

@ -0,0 +1,69 @@
{
"entity": {
"sensor": {
"voltage_a": {
"name": "Voltage A"
},
"voltage_b": {
"name": "Voltage B"
},
"voltage_c": {
"name": "Voltage C"
},
"current_a": {
"name": "Current A"
},
"current_b": {
"name": "Current B"
},
"current_c": {
"name": "Current C"
},
"power_a": {
"name": "Power A"
},
"power_b": {
"name": "Power B"
},
"power_c": {
"name": "Power C"
},
"import_energy_a": {
"name": "ImportEnergy A"
},
"import_energy_b": {
"name": "ImportEnergy B"
},
"import_energy_c": {
"name": "ImportEnergy C"
},
"export_grid_a": {
"name": "ExportGrid A"
},
"export_grid_b": {
"name": "ExportGrid B"
},
"export_grid_c": {
"name": "ExportGrid C"
},
"frequency_a": {
"name": "Frequency A"
},
"frequency_b": {
"name": "Frequency B"
},
"frequency_c": {
"name": "Frequency C"
},
"pf_a": {
"name": "PF A"
},
"pf_b": {
"name": "PF B"
},
"pf_c": {
"name": "PF C"
}
}
}
}

View File

@ -1051,7 +1051,7 @@ huawei-lte-api==1.7.3
hyperion-py==0.7.5
# homeassistant.components.iammeter
iammeter==0.1.7
iammeter==0.2.1
# homeassistant.components.iaqualink
iaqualink==0.5.0