1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Bump pyvlx to 0.2.29 (#162829)

This commit is contained in:
wollew
2026-02-11 21:10:44 +01:00
committed by GitHub
parent 5f01124c74
commit 98602bd311
7 changed files with 173 additions and 24 deletions

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Any
from pyvlx import Intensity, Light
from pyvlx import Intensity, Light, OnOffLight
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.core import HomeAssistant
@@ -26,7 +26,7 @@ async def async_setup_entry(
async_add_entities(
VeluxLight(node, config_entry.entry_id)
for node in pyvlx.nodes
if isinstance(node, Light)
if isinstance(node, (Light, OnOffLight))
)
@@ -42,7 +42,7 @@ class VeluxLight(VeluxEntity, LightEntity):
@property
def brightness(self):
"""Return the current brightness."""
return int((100 - self.node.intensity.intensity_percent) * 255 / 100)
return int(self.node.intensity.intensity_percent * 255 / 100)
@property
def is_on(self):
@@ -53,7 +53,7 @@ class VeluxLight(VeluxEntity, LightEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the light to turn on."""
if ATTR_BRIGHTNESS in kwargs:
intensity_percent = int(100 - kwargs[ATTR_BRIGHTNESS] / 255 * 100)
intensity_percent = int(kwargs[ATTR_BRIGHTNESS] / 255 * 100)
await self.node.set_intensity(
Intensity(intensity_percent=intensity_percent),
wait_for_completion=True,

View File

@@ -13,5 +13,5 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["pyvlx"],
"requirements": ["pyvlx==0.2.28"]
"requirements": ["pyvlx==0.2.29"]
}

2
requirements_all.txt generated
View File

@@ -2682,7 +2682,7 @@ pyvesync==3.4.1
pyvizio==0.1.61
# homeassistant.components.velux
pyvlx==0.2.28
pyvlx==0.2.29
# homeassistant.components.volumio
pyvolumio==0.1.5

View File

@@ -2260,7 +2260,7 @@ pyvesync==3.4.1
pyvizio==0.1.61
# homeassistant.components.velux
pyvlx==0.2.28
pyvlx==0.2.29
# homeassistant.components.volumio
pyvolumio==0.1.5

View File

@@ -4,11 +4,9 @@ from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from pyvlx.lightening_device import Light
from pyvlx.opening_device import Blind, Window
from pyvlx import Blind, Light, OnOffLight, Scene, Window
from homeassistant.components.velux import DOMAIN
from homeassistant.components.velux.scene import PyVLXScene as Scene
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant
@@ -104,6 +102,18 @@ def mock_light() -> AsyncMock:
return light
# a light without intensity support (e.g., a simple on/off light)
@pytest.fixture
def mock_onoff_light() -> AsyncMock:
"""Create a mock Velux light."""
light = AsyncMock(spec=OnOffLight, autospec=True)
light.name = "Test On Off Light"
light.serial_number = "0816"
light.intensity = MagicMock()
light.pyvlx = MagicMock()
return light
# fixture to create all other cover types via parameterization
@pytest.fixture
def mock_cover_type(request: pytest.FixtureRequest) -> AsyncMock:
@@ -122,6 +132,7 @@ def mock_cover_type(request: pytest.FixtureRequest) -> AsyncMock:
def mock_pyvlx(
mock_scene: AsyncMock,
mock_light: AsyncMock,
mock_onoff_light: AsyncMock,
mock_window: AsyncMock,
mock_blind: AsyncMock,
request: pytest.FixtureRequest,
@@ -138,7 +149,13 @@ def mock_pyvlx(
if hasattr(request, "param"):
pyvlx.nodes = [request.getfixturevalue(request.param)]
else:
pyvlx.nodes = [mock_light, mock_blind, mock_window, mock_cover_type]
pyvlx.nodes = [
mock_light,
mock_onoff_light,
mock_blind,
mock_window,
mock_cover_type,
]
pyvlx.scenes = [mock_scene]

View File

@@ -0,0 +1,119 @@
# serializer version: 1
# name: test_light_setup[mock_light][light.test_light-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.test_light',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'velux',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '0815',
'unit_of_measurement': None,
})
# ---
# name: test_light_setup[mock_light][light.test_light-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': None,
'color_mode': None,
'friendly_name': 'Test Light',
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.test_light',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_light_setup[mock_onoff_light][light.test_on_off_light-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.test_on_off_light',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'velux',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '0816',
'unit_of_measurement': None,
})
# ---
# name: test_light_setup[mock_onoff_light][light.test_on_off_light-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': None,
'color_mode': None,
'friendly_name': 'Test On Off Light',
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.test_on_off_light',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@@ -16,7 +16,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import update_callback_entity
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, SnapshotAssertion, snapshot_platform
# Apply setup_integration fixture to all tests in this module
pytestmark = pytest.mark.usefixtures("setup_integration")
@@ -28,21 +28,34 @@ def platform() -> Platform:
return Platform.LIGHT
@pytest.mark.parametrize(
"mock_pyvlx", ["mock_light", "mock_onoff_light"], indirect=True
)
async def test_light_setup(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Snapshot the entity and validate registry metadata for light entities."""
await snapshot_platform(
hass,
entity_registry,
snapshot,
mock_config_entry.entry_id,
)
async def test_light_device_association(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
mock_light: AsyncMock,
) -> None:
"""Test light entity setup and device association."""
"""Test light device association."""
test_entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"
# Check that the entity exists and its name matches the node name (the light is the main feature).
state = hass.states.get(test_entity_id)
assert state is not None
assert state.attributes.get("friendly_name") == mock_light.name
# Get entity + device entry
entity_entry = entity_registry.async_get(test_entity_id)
assert entity_entry is not None
@@ -137,7 +150,7 @@ async def test_light_brightness_and_is_on(
entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"
# Set initial intensity values
mock_light.intensity.intensity_percent = 20 # 20% "intensity" -> 80% brightness
mock_light.intensity.intensity_percent = 20 # 20% "intensity" -> 20% brightness
mock_light.intensity.off = False
mock_light.intensity.known = True
@@ -146,8 +159,8 @@ async def test_light_brightness_and_is_on(
state = hass.states.get(entity_id)
assert state is not None
# brightness = int((100 - 20) * 255 / 100) = int(204)
assert state.attributes.get("brightness") == 204
# brightness = int(20 * 255 / 100) = int(51)
assert state.attributes.get("brightness") == 51
assert state.state == "on"
# Mark as off
@@ -161,7 +174,7 @@ async def test_light_brightness_and_is_on(
async def test_light_turn_on_with_brightness_uses_set_intensity(
hass: HomeAssistant, mock_light: AsyncMock
) -> None:
"""Turning on with brightness calls set_intensity with inverted percent."""
"""Turning on with brightness calls set_intensity with percent."""
entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"
@@ -180,8 +193,8 @@ async def test_light_turn_on_with_brightness_uses_set_intensity(
# Inspect the intensity argument (first positional)
args, kwargs = mock_light.set_intensity.await_args
intensity_obj = args[0]
# brightness 51 -> 20% normalized -> intensity_percent = 80
assert intensity_obj.intensity_percent == 80
# brightness 51 -> 20% normalized -> intensity_percent = 20
assert intensity_obj.intensity_percent == 20
assert kwargs.get("wait_for_completion") is True