Add number platform to ohme (#136271)

Co-authored-by: Shay Levy <levyshay1@gmail.com>
This commit is contained in:
Dan Raper 2025-01-22 21:19:54 +00:00 committed by GitHub
parent 52f77626f7
commit e3c836aa7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 201 additions and 1 deletions

View File

@ -3,4 +3,4 @@
from homeassistant.const import Platform
DOMAIN = "ohme"
PLATFORMS = [Platform.BUTTON, Platform.SENSOR, Platform.SWITCH]
PLATFORMS = [Platform.BUTTON, Platform.NUMBER, Platform.SENSOR, Platform.SWITCH]

View File

@ -5,6 +5,11 @@
"default": "mdi:check-decagram"
}
},
"number": {
"target_percentage": {
"default": "mdi:battery-heart"
}
},
"sensor": {
"status": {
"default": "mdi:car",

View File

@ -0,0 +1,77 @@
"""Platform for number."""
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from ohme import ApiException, OhmeApiClient
from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import OhmeConfigEntry
from .const import DOMAIN
from .entity import OhmeEntity, OhmeEntityDescription
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
class OhmeNumberDescription(OhmeEntityDescription, NumberEntityDescription):
"""Class describing Ohme number entities."""
set_fn: Callable[[OhmeApiClient, float], Awaitable[None]]
value_fn: Callable[[OhmeApiClient], float]
NUMBER_DESCRIPTION = [
OhmeNumberDescription(
key="target_percentage",
translation_key="target_percentage",
value_fn=lambda client: client.target_soc,
set_fn=lambda client, value: client.async_set_target(target_percent=value),
native_min_value=0,
native_max_value=100,
native_step=1,
native_unit_of_measurement=PERCENTAGE,
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: OhmeConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up numbers."""
coordinators = config_entry.runtime_data
coordinator = coordinators.charge_session_coordinator
async_add_entities(
OhmeNumber(coordinator, description)
for description in NUMBER_DESCRIPTION
if description.is_supported_fn(coordinator.client)
)
class OhmeNumber(OhmeEntity, NumberEntity):
"""Generic number entity for Ohme."""
entity_description: OhmeNumberDescription
@property
def native_value(self) -> float:
"""Return the current value of the number."""
return self.entity_description.value_fn(self.coordinator.client)
async def async_set_native_value(self, value: float) -> None:
"""Set the number value."""
try:
await self.entity_description.set_fn(self.coordinator.client, value)
except ApiException as e:
raise HomeAssistantError(
translation_key="api_failed", translation_domain=DOMAIN
) from e
await self.coordinator.async_request_refresh()

View File

@ -50,6 +50,11 @@
"name": "Approve charge"
}
},
"number": {
"target_percentage": {
"name": "Target percentage"
}
},
"sensor": {
"status": {
"name": "Status",

View File

@ -54,6 +54,7 @@ def mock_client():
client.status = ChargerStatus.CHARGING
client.power = ChargerPower(0, 0, 0, 0)
client.target_soc = 50
client.battery = 80
client.serial = "chargerid"
client.ct_connected = True

View File

@ -0,0 +1,57 @@
# serializer version: 1
# name: test_numbers[number.ohme_home_pro_target_percentage-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 100,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': None,
'entity_id': 'number.ohme_home_pro_target_percentage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Target percentage',
'platform': 'ohme',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'target_percentage',
'unique_id': 'chargerid_target_percentage',
'unit_of_measurement': '%',
})
# ---
# name: test_numbers[number.ohme_home_pro_target_percentage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Ohme Home Pro Target percentage',
'max': 100,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'number.ohme_home_pro_target_percentage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '50',
})
# ---

View File

@ -0,0 +1,55 @@
"""Tests for numbers."""
from unittest.mock import MagicMock, patch
from syrupy import SnapshotAssertion
from homeassistant.components.number import (
ATTR_VALUE,
DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
async def test_numbers(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_config_entry: MockConfigEntry,
mock_client: MagicMock,
) -> None:
"""Test the Ohme sensors."""
with patch("homeassistant.components.ohme.PLATFORMS", [Platform.NUMBER]):
await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_set_number(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_client: MagicMock,
) -> None:
"""Test the number set."""
await setup_integration(hass, mock_config_entry)
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
service_data={
ATTR_VALUE: 100,
},
target={
ATTR_ENTITY_ID: "number.ohme_home_pro_target_percentage",
},
blocking=True,
)
assert len(mock_client.async_set_target.mock_calls) == 1