Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add height sensor to Idasen Desk integration #103324

Merged
merged 16 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/idasen_desk/__init__.py
Expand Up @@ -24,7 +24,7 @@

from .const import DOMAIN

PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.COVER]
PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.COVER, Platform.SENSOR]

_LOGGER = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/idasen_desk/manifest.json
Expand Up @@ -11,5 +11,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/idasen_desk",
"iot_class": "local_push",
"requirements": ["idasen-ha==2.3"]
"requirements": ["idasen-ha==2.4"]
}
100 changes: 100 additions & 0 deletions homeassistant/components/idasen_desk/sensor.py
@@ -0,0 +1,100 @@
"""Representation of Idasen Desk sensors."""
from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from typing import Any

from homeassistant import config_entries
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import UnitOfLength
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import DeskData, IdasenDeskCoordinator
from .const import DOMAIN


@dataclass
class IdasenDeskSensorDescriptionMixin:
"""Required values for IdasenDesk sensors."""

value_fn: Callable[[IdasenDeskCoordinator], float | None]


@dataclass
class IdasenDeskSensorDescription(
SensorEntityDescription,
IdasenDeskSensorDescriptionMixin,
):
"""Class describing IdasenDesk sensor entities."""


SENSORS = (
IdasenDeskSensorDescription(
key="height",
translation_key="height",
icon="mdi:arrow-up-down",
abmantis marked this conversation as resolved.
Show resolved Hide resolved
native_unit_of_measurement=UnitOfLength.METERS,
device_class=SensorDeviceClass.DISTANCE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
suggested_display_precision=3,
value_fn=lambda coordinator: coordinator.desk.height,
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: config_entries.ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Idasen Desk sensors."""
data: DeskData = hass.data[DOMAIN][entry.entry_id]
async_add_entities(
IdasenDeskSensor(
data.address, data.device_info, data.coordinator, sensor_description
)
for sensor_description in SENSORS
)


class IdasenDeskSensor(CoordinatorEntity, SensorEntity):
"""IdasenDesk sensor."""

entity_description: IdasenDeskSensorDescription
_attr_has_entity_name = True

abmantis marked this conversation as resolved.
Show resolved Hide resolved
def __init__(
self,
address: str,
device_info: DeviceInfo,
coordinator: IdasenDeskCoordinator,
description: IdasenDeskSensorDescription,
) -> None:
"""Initialize the IdasenDesk sensor entity."""
super().__init__(coordinator)
self.entity_description = description

self._attr_unique_id = f"{description.key}-{address}"
self._attr_device_info = device_info
self._address = address

async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
self._handle_coordinator_update()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will make an extra state update besides the default state update at the end of entity addition. We should separate the native value attribute update from the state update if we need to update the native value here.


@callback
def _handle_coordinator_update(self, *args: Any) -> None:
"""Handle data update."""
self._attr_native_value = self.entity_description.value_fn(self.coordinator)
super()._handle_coordinator_update()
7 changes: 7 additions & 0 deletions homeassistant/components/idasen_desk/strings.json
Expand Up @@ -19,5 +19,12 @@
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"no_devices_found": "No unconfigured devices found. Make sure that the desk is in Bluetooth pairing mode. Enter pairing mode by pressing the small button with the Bluetooth logo on the controller for about 3 seconds, until it starts blinking."
}
},
"entity": {
"sensor": {
"height": {
"name": "Height"
}
}
}
}
2 changes: 1 addition & 1 deletion requirements_all.txt
Expand Up @@ -1056,7 +1056,7 @@ ical==5.1.0
icmplib==3.0

# homeassistant.components.idasen_desk
idasen-ha==2.3
idasen-ha==2.4

# homeassistant.components.network
ifaddr==0.2.0
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Expand Up @@ -836,7 +836,7 @@ ical==5.1.0
icmplib==3.0

# homeassistant.components.idasen_desk
idasen-ha==2.3
idasen-ha==2.4

# homeassistant.components.network
ifaddr==0.2.0
Expand Down
1 change: 1 addition & 0 deletions tests/components/idasen_desk/conftest.py
Expand Up @@ -55,6 +55,7 @@ async def mock_move_down():
mock_desk.move_up = AsyncMock(side_effect=mock_move_up)
mock_desk.move_down = AsyncMock(side_effect=mock_move_down)
mock_desk.stop = AsyncMock()
mock_desk.height = 1
mock_desk.height_percent = 60
mock_desk.is_moving = False
mock_desk.address = "AA:BB:CC:DD:EE:FF"
Expand Down
27 changes: 27 additions & 0 deletions tests/components/idasen_desk/test_sensors.py
@@ -0,0 +1,27 @@
"""Test the IKEA Idasen Desk sensors."""
from unittest.mock import MagicMock

from homeassistant.core import HomeAssistant

from . import init_integration


async def test_height_sensor(
hass: HomeAssistant,
mock_desk_api: MagicMock,
entity_registry_enabled_by_default: None,
) -> None:
"""Test height sensor."""
await init_integration(hass)

entity_id = "sensor.test_height"
state = hass.states.get(entity_id)
assert state
assert state.state == "1"

mock_desk_api.height = 1.2
mock_desk_api.trigger_update_callback(None)

state = hass.states.get(entity_id)
assert state
assert state.state == "1.2"