mirror of
https://github.com/home-assistant/core.git
synced 2026-05-04 03:51:12 +02:00
Use motor rotation mode in Tuya clkg covers (curtain) (#151767)
This commit is contained in:
@@ -22,7 +22,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from . import TuyaConfigEntry
|
||||
from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
|
||||
from .entity import TuyaEntity
|
||||
from .models import IntegerTypeData
|
||||
from .models import EnumTypeData, IntegerTypeData
|
||||
from .util import get_dpcode
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class TuyaCoverEntityDescription(CoverEntityDescription):
|
||||
open_instruction_value: str = "open"
|
||||
close_instruction_value: str = "close"
|
||||
stop_instruction_value: str = "stop"
|
||||
motor_reverse_mode: DPCode | None = None
|
||||
|
||||
|
||||
COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = {
|
||||
@@ -124,6 +125,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = {
|
||||
translation_key="curtain",
|
||||
current_position=DPCode.PERCENT_CONTROL,
|
||||
set_position=DPCode.PERCENT_CONTROL,
|
||||
motor_reverse_mode=DPCode.CONTROL_BACK_MODE,
|
||||
device_class=CoverDeviceClass.CURTAIN,
|
||||
),
|
||||
TuyaCoverEntityDescription(
|
||||
@@ -132,6 +134,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = {
|
||||
translation_placeholders={"index": "2"},
|
||||
current_position=DPCode.PERCENT_CONTROL_2,
|
||||
set_position=DPCode.PERCENT_CONTROL_2,
|
||||
motor_reverse_mode=DPCode.CONTROL_BACK_MODE,
|
||||
device_class=CoverDeviceClass.CURTAIN,
|
||||
),
|
||||
),
|
||||
@@ -188,6 +191,7 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
_current_position: IntegerTypeData | None = None
|
||||
_set_position: IntegerTypeData | None = None
|
||||
_tilt: IntegerTypeData | None = None
|
||||
_motor_reverse_mode_enum: EnumTypeData | None = None
|
||||
entity_description: TuyaCoverEntityDescription
|
||||
|
||||
def __init__(
|
||||
@@ -242,6 +246,27 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
self._attr_supported_features |= CoverEntityFeature.SET_TILT_POSITION
|
||||
self._tilt = int_type
|
||||
|
||||
# Determine type to use for checking motor reverse mode
|
||||
if (motor_mode := description.motor_reverse_mode) and (
|
||||
enum_type := self.find_dpcode(
|
||||
motor_mode,
|
||||
dptype=DPType.ENUM,
|
||||
prefer_function=True,
|
||||
)
|
||||
):
|
||||
self._motor_reverse_mode_enum = enum_type
|
||||
|
||||
@property
|
||||
def _is_motor_forward(self) -> bool:
|
||||
"""Check if the cover direction should be reversed based on motor_reverse_mode.
|
||||
|
||||
If the motor is "forward" (=default) then the positions need to be reversed.
|
||||
"""
|
||||
return not (
|
||||
self._motor_reverse_mode_enum
|
||||
and self.device.status.get(self._motor_reverse_mode_enum.dpcode) == "back"
|
||||
)
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int | None:
|
||||
"""Return cover current position."""
|
||||
@@ -252,7 +277,9 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
return None
|
||||
|
||||
return round(
|
||||
self._current_position.remap_value_to(position, 0, 100, reverse=True)
|
||||
self._current_position.remap_value_to(
|
||||
position, 0, 100, reverse=self._is_motor_forward
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -307,7 +334,9 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
{
|
||||
"code": self._set_position.dpcode,
|
||||
"value": round(
|
||||
self._set_position.remap_value_from(100, 0, 100, reverse=True),
|
||||
self._set_position.remap_value_from(
|
||||
100, 0, 100, reverse=self._is_motor_forward
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -331,7 +360,9 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
{
|
||||
"code": self._set_position.dpcode,
|
||||
"value": round(
|
||||
self._set_position.remap_value_from(0, 0, 100, reverse=True),
|
||||
self._set_position.remap_value_from(
|
||||
0, 0, 100, reverse=self._is_motor_forward
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -350,7 +381,10 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
"code": self._set_position.dpcode,
|
||||
"value": round(
|
||||
self._set_position.remap_value_from(
|
||||
kwargs[ATTR_POSITION], 0, 100, reverse=True
|
||||
kwargs[ATTR_POSITION],
|
||||
0,
|
||||
100,
|
||||
reverse=self._is_motor_forward,
|
||||
)
|
||||
),
|
||||
}
|
||||
@@ -380,7 +414,10 @@ class TuyaCoverEntity(TuyaEntity, CoverEntity):
|
||||
"code": self._tilt.dpcode,
|
||||
"value": round(
|
||||
self._tilt.remap_value_from(
|
||||
kwargs[ATTR_TILT_POSITION], 0, 100, reverse=True
|
||||
kwargs[ATTR_TILT_POSITION],
|
||||
0,
|
||||
100,
|
||||
reverse=self._is_motor_forward,
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@
|
||||
# name: test_platform_setup_and_discovery[cover.roller_shutter_living_room_curtain-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'current_position': 25,
|
||||
'current_position': 75,
|
||||
'device_class': 'curtain',
|
||||
'friendly_name': 'Roller shutter Living Room Curtain',
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
|
||||
@@ -204,3 +204,110 @@ async def test_set_tilt_position_not_supported(
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mock_device_code",
|
||||
["clkg_wltqkykhni0papzj"],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("initial_percent_control", "expected_state", "expected_position"),
|
||||
[
|
||||
(0, "closed", 0),
|
||||
(25, "open", 25),
|
||||
(50, "open", 50),
|
||||
(75, "open", 75),
|
||||
(100, "open", 100),
|
||||
],
|
||||
)
|
||||
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.COVER])
|
||||
async def test_clkg_wltqkykhni0papzj_state(
|
||||
hass: HomeAssistant,
|
||||
mock_manager: Manager,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_device: CustomerDevice,
|
||||
initial_percent_control: int,
|
||||
expected_state: str,
|
||||
expected_position: int,
|
||||
) -> None:
|
||||
"""Test cover position for wltqkykhni0papzj device.
|
||||
|
||||
See https://github.com/home-assistant/core/issues/151635
|
||||
percent_control == 0 is when my roller shutter is completely open (meaning up)
|
||||
percent_control == 100 is when my roller shutter is completely closed (meaning down)
|
||||
"""
|
||||
entity_id = "cover.roller_shutter_living_room_curtain"
|
||||
mock_device.status["percent_control"] = initial_percent_control
|
||||
|
||||
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None, f"{entity_id} does not exist"
|
||||
assert state.state == expected_state
|
||||
assert state.attributes[ATTR_CURRENT_POSITION] == expected_position
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mock_device_code",
|
||||
["clkg_wltqkykhni0papzj"],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("service_name", "service_kwargs", "expected_commands"),
|
||||
[
|
||||
(
|
||||
SERVICE_OPEN_COVER,
|
||||
{},
|
||||
[
|
||||
{"code": "control", "value": "open"},
|
||||
{"code": "percent_control", "value": 100},
|
||||
],
|
||||
),
|
||||
(
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
{ATTR_POSITION: 25},
|
||||
[
|
||||
{"code": "percent_control", "value": 25},
|
||||
],
|
||||
),
|
||||
(
|
||||
SERVICE_CLOSE_COVER,
|
||||
{},
|
||||
[
|
||||
{"code": "control", "value": "close"},
|
||||
{"code": "percent_control", "value": 0},
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.COVER])
|
||||
async def test_clkg_wltqkykhni0papzj_action(
|
||||
hass: HomeAssistant,
|
||||
mock_manager: Manager,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_device: CustomerDevice,
|
||||
service_name: str,
|
||||
service_kwargs: dict,
|
||||
expected_commands: list[dict],
|
||||
) -> None:
|
||||
"""Test cover position for wltqkykhni0papzj device.
|
||||
|
||||
See https://github.com/home-assistant/core/issues/151635
|
||||
percent_control == 0 is when my roller shutter is completely open (meaning up)
|
||||
percent_control == 100 is when my roller shutter is completely closed (meaning down)
|
||||
"""
|
||||
entity_id = "cover.roller_shutter_living_room_curtain"
|
||||
mock_device.status["percent_control"] = 50
|
||||
|
||||
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
service_name,
|
||||
{ATTR_ENTITY_ID: entity_id, **service_kwargs},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_manager.send_commands.assert_called_once_with(
|
||||
mock_device.id,
|
||||
expected_commands,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user