Skip to content

Commit

Permalink
Add Plugwise number platform (home-assistant#74655)
Browse files Browse the repository at this point in the history
  • Loading branch information
bouwew authored Jul 13, 2022
1 parent b7a6f4e commit 34f1d5e
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 4 deletions.
3 changes: 2 additions & 1 deletion homeassistant/components/plugwise/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
PLATFORMS_GATEWAY: Final[list[str]] = [
Platform.BINARY_SENSOR,
Platform.CLIMATE,
Platform.SENSOR,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
]
ZEROCONF_MAP: Final[dict[str, str]] = {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/plugwise/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"domain": "plugwise",
"name": "Plugwise",
"documentation": "https://www.home-assistant.io/integrations/plugwise",
"requirements": ["plugwise==0.18.5"],
"requirements": ["plugwise==0.18.6"],
"codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"],
"zeroconf": ["_plugwise._tcp.local."],
"config_flow": true,
Expand Down
115 changes: 115 additions & 0 deletions homeassistant/components/plugwise/number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""Number platform for Plugwise integration."""
from __future__ import annotations

from collections.abc import Awaitable, Callable
from dataclasses import dataclass

from plugwise import Smile

from homeassistant.components.number import (
NumberDeviceClass,
NumberEntity,
NumberEntityDescription,
NumberMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import TEMP_CELSIUS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN
from .coordinator import PlugwiseDataUpdateCoordinator
from .entity import PlugwiseEntity


@dataclass
class PlugwiseEntityDescriptionMixin:
"""Mixin values for Plugwse entities."""

command: Callable[[Smile, float], Awaitable[None]]


@dataclass
class PlugwiseNumberEntityDescription(
NumberEntityDescription, PlugwiseEntityDescriptionMixin
):
"""Class describing Plugwise Number entities."""


NUMBER_TYPES = (
PlugwiseNumberEntityDescription(
key="maximum_boiler_temperature",
command=lambda api, value: api.set_max_boiler_temperature(value),
device_class=NumberDeviceClass.TEMPERATURE,
name="Maximum Boiler Temperature Setpoint",
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=TEMP_CELSIUS,
),
)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Plugwise number platform."""

coordinator: PlugwiseDataUpdateCoordinator = hass.data[DOMAIN][
config_entry.entry_id
]

entities: list[PlugwiseNumberEntity] = []
for device_id, device in coordinator.data.devices.items():
for description in NUMBER_TYPES:
if description.key in device:
entities.append(
PlugwiseNumberEntity(coordinator, device_id, description)
)

async_add_entities(entities)


class PlugwiseNumberEntity(PlugwiseEntity, NumberEntity):
"""Representation of a Plugwise number."""

entity_description: PlugwiseNumberEntityDescription

def __init__(
self,
coordinator: PlugwiseDataUpdateCoordinator,
device_id: str,
description: PlugwiseNumberEntityDescription,
) -> None:
"""Initiate Plugwise Number."""
super().__init__(coordinator, device_id)
self.entity_description = description
self._attr_unique_id = f"{device_id}-{description.key}"
self._attr_name = (f"{self.device['name']} {description.name}").lstrip()
self._attr_mode = NumberMode.BOX

@property
def native_step(self) -> float:
"""Return the setpoint step value."""
return max(self.device["resolution"], 1)

@property
def native_value(self) -> float:
"""Return the present setpoint value."""
return self.device[self.entity_description.key]

@property
def native_min_value(self) -> float:
"""Return the setpoint min. value."""
return self.device["lower_bound"]

@property
def native_max_value(self) -> float:
"""Return the setpoint max. value."""
return self.device["upper_bound"]

async def async_set_native_value(self, value: float) -> None:
"""Change to the new setpoint value."""
await self.entity_description.command(self.coordinator.api, value)
await self.coordinator.async_request_refresh()
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13

# homeassistant.components.plugwise
plugwise==0.18.5
plugwise==0.18.6

# homeassistant.components.plum_lightpad
plumlightpad==0.0.11
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13

# homeassistant.components.plugwise
plugwise==0.18.5
plugwise==0.18.6

# homeassistant.components.plum_lightpad
plumlightpad==0.0.11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"model": "Generic heater",
"name": "OpenTherm",
"vendor": "Techneco",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 1.0,
"maximum_boiler_temperature": 60.0,
"binary_sensors": {
"dhw_state": false,
Expand Down
40 changes: 40 additions & 0 deletions tests/components/plugwise/test_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Tests for the Plugwise Number integration."""

from unittest.mock import MagicMock

from homeassistant.components.number import (
ATTR_VALUE,
DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant

from tests.common import MockConfigEntry


async def test_anna_number_entities(
hass: HomeAssistant, mock_smile_anna: MagicMock, init_integration: MockConfigEntry
) -> None:
"""Test creation of a number."""
state = hass.states.get("number.opentherm_maximum_boiler_temperature_setpoint")
assert state
assert float(state.state) == 60.0


async def test_anna_max_boiler_temp_change(
hass: HomeAssistant, mock_smile_anna: MagicMock, init_integration: MockConfigEntry
) -> None:
"""Test changing of number entities."""
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.opentherm_maximum_boiler_temperature_setpoint",
ATTR_VALUE: 65,
},
blocking=True,
)

assert mock_smile_anna.set_max_boiler_temperature.call_count == 1
mock_smile_anna.set_max_boiler_temperature.assert_called_with(65)

0 comments on commit 34f1d5e

Please sign in to comment.