Skip to content

Commit

Permalink
Add number platform to ohme (#136271)
Browse files Browse the repository at this point in the history
Co-authored-by: Shay Levy <[email protected]>
  • Loading branch information
dan-r and thecode authored Jan 22, 2025
1 parent 52f7762 commit e3c836a
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 1 deletion.
2 changes: 1 addition & 1 deletion homeassistant/components/ohme/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
5 changes: 5 additions & 0 deletions homeassistant/components/ohme/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"default": "mdi:check-decagram"
}
},
"number": {
"target_percentage": {
"default": "mdi:battery-heart"
}
},
"sensor": {
"status": {
"default": "mdi:car",
Expand Down
77 changes: 77 additions & 0 deletions homeassistant/components/ohme/number.py
Original file line number Diff line number Diff line change
@@ -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()
5 changes: 5 additions & 0 deletions homeassistant/components/ohme/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"name": "Approve charge"
}
},
"number": {
"target_percentage": {
"name": "Target percentage"
}
},
"sensor": {
"status": {
"name": "Status",
Expand Down
1 change: 1 addition & 0 deletions tests/components/ohme/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
57 changes: 57 additions & 0 deletions tests/components/ohme/snapshots/test_number.ambr
Original file line number Diff line number Diff line change
@@ -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',
})
# ---
55 changes: 55 additions & 0 deletions tests/components/ohme/test_number.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit e3c836a

Please sign in to comment.