mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Filter unsupported parameters from light service calls (#51084)
* Filter unsupported parameters from light service calls * Silence pylint * Fix deconz tests * Fix mqtt tests * Fix scene tests * Fix trådfri emulated CT * Fix mqtt tests
This commit is contained in:
parent
9b13350b01
commit
154c849eac
@ -247,11 +247,52 @@ def preprocess_turn_on_alternatives(hass, params):
|
|||||||
params[ATTR_BRIGHTNESS] = round(255 * brightness_pct / 100)
|
params[ATTR_BRIGHTNESS] = round(255 * brightness_pct / 100)
|
||||||
|
|
||||||
|
|
||||||
def filter_turn_off_params(params):
|
def filter_turn_off_params(light, params):
|
||||||
"""Filter out params not used in turn off."""
|
"""Filter out params not used in turn off."""
|
||||||
|
supported_features = light.supported_features
|
||||||
|
|
||||||
|
if not supported_features & SUPPORT_FLASH:
|
||||||
|
params.pop(ATTR_FLASH, None)
|
||||||
|
if not supported_features & SUPPORT_TRANSITION:
|
||||||
|
params.pop(ATTR_TRANSITION, None)
|
||||||
|
|
||||||
return {k: v for k, v in params.items() if k in (ATTR_TRANSITION, ATTR_FLASH)}
|
return {k: v for k, v in params.items() if k in (ATTR_TRANSITION, ATTR_FLASH)}
|
||||||
|
|
||||||
|
|
||||||
|
def filter_turn_on_params(light, params):
|
||||||
|
"""Filter out params not used in turn off."""
|
||||||
|
supported_features = light.supported_features
|
||||||
|
|
||||||
|
if not supported_features & SUPPORT_EFFECT:
|
||||||
|
params.pop(ATTR_EFFECT, None)
|
||||||
|
if not supported_features & SUPPORT_FLASH:
|
||||||
|
params.pop(ATTR_FLASH, None)
|
||||||
|
if not supported_features & SUPPORT_TRANSITION:
|
||||||
|
params.pop(ATTR_TRANSITION, None)
|
||||||
|
if not supported_features & SUPPORT_WHITE_VALUE:
|
||||||
|
params.pop(ATTR_WHITE_VALUE, None)
|
||||||
|
|
||||||
|
supported_color_modes = (
|
||||||
|
light._light_internal_supported_color_modes # pylint:disable=protected-access
|
||||||
|
)
|
||||||
|
if not brightness_supported(supported_color_modes):
|
||||||
|
params.pop(ATTR_BRIGHTNESS, None)
|
||||||
|
if COLOR_MODE_COLOR_TEMP not in supported_color_modes:
|
||||||
|
params.pop(ATTR_COLOR_TEMP, None)
|
||||||
|
if COLOR_MODE_HS not in supported_color_modes:
|
||||||
|
params.pop(ATTR_HS_COLOR, None)
|
||||||
|
if COLOR_MODE_RGB not in supported_color_modes:
|
||||||
|
params.pop(ATTR_RGB_COLOR, None)
|
||||||
|
if COLOR_MODE_RGBW not in supported_color_modes:
|
||||||
|
params.pop(ATTR_RGBW_COLOR, None)
|
||||||
|
if COLOR_MODE_RGBWW not in supported_color_modes:
|
||||||
|
params.pop(ATTR_RGBWW_COLOR, None)
|
||||||
|
if COLOR_MODE_XY not in supported_color_modes:
|
||||||
|
params.pop(ATTR_XY_COLOR, None)
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config): # noqa: C901
|
async def async_setup(hass, config): # noqa: C901
|
||||||
"""Expose light control via state machine and services."""
|
"""Expose light control via state machine and services."""
|
||||||
component = hass.data[DOMAIN] = EntityComponent(
|
component = hass.data[DOMAIN] = EntityComponent(
|
||||||
@ -373,7 +414,7 @@ async def async_setup(hass, config): # noqa: C901
|
|||||||
if params.get(ATTR_BRIGHTNESS) == 0:
|
if params.get(ATTR_BRIGHTNESS) == 0:
|
||||||
await async_handle_light_off_service(light, call)
|
await async_handle_light_off_service(light, call)
|
||||||
else:
|
else:
|
||||||
await light.async_turn_on(**params)
|
await light.async_turn_on(**filter_turn_on_params(light, params))
|
||||||
|
|
||||||
async def async_handle_light_off_service(light, call):
|
async def async_handle_light_off_service(light, call):
|
||||||
"""Handle turning off a light."""
|
"""Handle turning off a light."""
|
||||||
@ -382,7 +423,7 @@ async def async_setup(hass, config): # noqa: C901
|
|||||||
if ATTR_TRANSITION not in params:
|
if ATTR_TRANSITION not in params:
|
||||||
profiles.apply_default(light.entity_id, True, params)
|
profiles.apply_default(light.entity_id, True, params)
|
||||||
|
|
||||||
await light.async_turn_off(**filter_turn_off_params(params))
|
await light.async_turn_off(**filter_turn_off_params(light, params))
|
||||||
|
|
||||||
async def async_handle_toggle_service(light, call):
|
async def async_handle_toggle_service(light, call):
|
||||||
"""Handle toggling a light."""
|
"""Handle toggling a light."""
|
||||||
|
@ -116,7 +116,7 @@ class TradfriLight(TradfriBaseDevice, LightEntity):
|
|||||||
if device.light_control.can_set_dimmer:
|
if device.light_control.can_set_dimmer:
|
||||||
_features |= SUPPORT_BRIGHTNESS
|
_features |= SUPPORT_BRIGHTNESS
|
||||||
if device.light_control.can_set_color:
|
if device.light_control.can_set_color:
|
||||||
_features |= SUPPORT_COLOR
|
_features |= SUPPORT_COLOR | SUPPORT_COLOR_TEMP
|
||||||
if device.light_control.can_set_temp:
|
if device.light_control.can_set_temp:
|
||||||
_features |= SUPPORT_COLOR_TEMP
|
_features |= SUPPORT_COLOR_TEMP
|
||||||
self._features = _features
|
self._features = _features
|
||||||
|
@ -179,7 +179,6 @@ async def test_lights_and_groups(hass, aioclient_mock, mock_deconz_websocket):
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert aioclient_mock.mock_calls[1][2] == {
|
assert aioclient_mock.mock_calls[1][2] == {
|
||||||
"ct": 2500,
|
|
||||||
"bri": 200,
|
"bri": 200,
|
||||||
"transitiontime": 50,
|
"transitiontime": 50,
|
||||||
"alert": "select",
|
"alert": "select",
|
||||||
|
@ -117,6 +117,16 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
ent1, ent2, ent3 = platform.ENTITIES
|
ent1, ent2, ent3 = platform.ENTITIES
|
||||||
|
ent1.supported_color_modes = [light.COLOR_MODE_HS]
|
||||||
|
ent3.supported_color_modes = [light.COLOR_MODE_HS]
|
||||||
|
ent1.supported_features = light.SUPPORT_TRANSITION
|
||||||
|
ent2.supported_features = (
|
||||||
|
light.SUPPORT_COLOR
|
||||||
|
| light.SUPPORT_EFFECT
|
||||||
|
| light.SUPPORT_TRANSITION
|
||||||
|
| light.SUPPORT_WHITE_VALUE
|
||||||
|
)
|
||||||
|
ent3.supported_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
|
||||||
|
|
||||||
# Test init
|
# Test init
|
||||||
assert light.is_on(hass, ent1.entity_id)
|
assert light.is_on(hass, ent1.entity_id)
|
||||||
@ -205,6 +215,7 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
{
|
{
|
||||||
ATTR_ENTITY_ID: ent2.entity_id,
|
ATTR_ENTITY_ID: ent2.entity_id,
|
||||||
|
light.ATTR_EFFECT: "fun_effect",
|
||||||
light.ATTR_RGB_COLOR: (255, 255, 255),
|
light.ATTR_RGB_COLOR: (255, 255, 255),
|
||||||
light.ATTR_WHITE_VALUE: 255,
|
light.ATTR_WHITE_VALUE: 255,
|
||||||
},
|
},
|
||||||
@ -215,6 +226,7 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
{
|
{
|
||||||
ATTR_ENTITY_ID: ent3.entity_id,
|
ATTR_ENTITY_ID: ent3.entity_id,
|
||||||
|
light.ATTR_FLASH: "short",
|
||||||
light.ATTR_XY_COLOR: (0.4, 0.6),
|
light.ATTR_XY_COLOR: (0.4, 0.6),
|
||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
@ -228,10 +240,14 @@ async def test_services(hass, mock_light_profiles, enable_custom_integrations):
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, data = ent2.last_call("turn_on")
|
_, data = ent2.last_call("turn_on")
|
||||||
assert data == {light.ATTR_HS_COLOR: (0, 0), light.ATTR_WHITE_VALUE: 255}
|
assert data == {
|
||||||
|
light.ATTR_EFFECT: "fun_effect",
|
||||||
|
light.ATTR_HS_COLOR: (0, 0),
|
||||||
|
light.ATTR_WHITE_VALUE: 255,
|
||||||
|
}
|
||||||
|
|
||||||
_, data = ent3.last_call("turn_on")
|
_, data = ent3.last_call("turn_on")
|
||||||
assert data == {light.ATTR_HS_COLOR: (71.059, 100)}
|
assert data == {light.ATTR_FLASH: "short", light.ATTR_HS_COLOR: (71.059, 100)}
|
||||||
|
|
||||||
# Ensure attributes are filtered when light is turned off
|
# Ensure attributes are filtered when light is turned off
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
@ -521,6 +537,8 @@ async def test_light_profiles(
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
ent1, _, _ = platform.ENTITIES
|
ent1, _, _ = platform.ENTITIES
|
||||||
|
ent1.supported_color_modes = [light.COLOR_MODE_HS]
|
||||||
|
ent1.supported_features = light.SUPPORT_TRANSITION
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN,
|
light.DOMAIN,
|
||||||
@ -556,6 +574,8 @@ async def test_default_profiles_group(
|
|||||||
mock_light_profiles[profile.name] = profile
|
mock_light_profiles[profile.name] = profile
|
||||||
|
|
||||||
ent, _, _ = platform.ENTITIES
|
ent, _, _ = platform.ENTITIES
|
||||||
|
ent.supported_color_modes = [light.COLOR_MODE_HS]
|
||||||
|
ent.supported_features = light.SUPPORT_TRANSITION
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True
|
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True
|
||||||
)
|
)
|
||||||
@ -661,6 +681,8 @@ async def test_default_profiles_light(
|
|||||||
mock_light_profiles[profile.name] = profile
|
mock_light_profiles[profile.name] = profile
|
||||||
|
|
||||||
dev = next(filter(lambda x: x.entity_id == "light.ceiling_2", platform.ENTITIES))
|
dev = next(filter(lambda x: x.entity_id == "light.ceiling_2", platform.ENTITIES))
|
||||||
|
dev.supported_color_modes = [light.COLOR_MODE_HS]
|
||||||
|
dev.supported_features = light.SUPPORT_TRANSITION
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
light.DOMAIN,
|
light.DOMAIN,
|
||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
@ -1625,3 +1647,157 @@ async def test_light_state_color_conversion(hass, enable_custom_integrations):
|
|||||||
assert state.attributes["hs_color"] == (240, 100)
|
assert state.attributes["hs_color"] == (240, 100)
|
||||||
assert state.attributes["rgb_color"] == (0, 0, 255)
|
assert state.attributes["rgb_color"] == (0, 0, 255)
|
||||||
assert state.attributes["xy_color"] == (0.136, 0.04)
|
assert state.attributes["xy_color"] == (0.136, 0.04)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_services_filter_parameters(
|
||||||
|
hass, mock_light_profiles, enable_custom_integrations
|
||||||
|
):
|
||||||
|
"""Test turn_on and turn_off filters unsupported parameters."""
|
||||||
|
platform = getattr(hass.components, "test.light")
|
||||||
|
|
||||||
|
platform.init()
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, light.DOMAIN, {light.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
ent1, _, _ = platform.ENTITIES
|
||||||
|
|
||||||
|
# turn off the light by setting brightness to 0, this should work even if the light
|
||||||
|
# doesn't support brightness
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True
|
||||||
|
)
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: ENTITY_MATCH_ALL, light.ATTR_BRIGHTNESS: 0},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not light.is_on(hass, ent1.entity_id)
|
||||||
|
|
||||||
|
# Ensure all unsupported attributes are filtered when light is turned on
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_BRIGHTNESS: 0,
|
||||||
|
light.ATTR_EFFECT: "fun_effect",
|
||||||
|
light.ATTR_FLASH: "short",
|
||||||
|
light.ATTR_TRANSITION: 10,
|
||||||
|
light.ATTR_WHITE_VALUE: 0,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_COLOR_TEMP: 153,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_HS_COLOR: (0, 0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_RGB_COLOR: (0, 0, 0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_RGBW_COLOR: (0, 0, 0, 0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_RGBWW_COLOR: (0, 0, 0, 0, 0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_XY_COLOR: (0, 0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
_, data = ent1.last_call("turn_on")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
# Ensure all unsupported attributes are filtered when light is turned off
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_BRIGHTNESS: 0,
|
||||||
|
light.ATTR_EFFECT: "fun_effect",
|
||||||
|
light.ATTR_FLASH: "short",
|
||||||
|
light.ATTR_TRANSITION: 10,
|
||||||
|
light.ATTR_WHITE_VALUE: 0,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not light.is_on(hass, ent1.entity_id)
|
||||||
|
|
||||||
|
_, data = ent1.last_call("turn_off")
|
||||||
|
assert data == {}
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
light.DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: ent1.entity_id,
|
||||||
|
light.ATTR_FLASH: "short",
|
||||||
|
light.ATTR_TRANSITION: 10,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not light.is_on(hass, ent1.entity_id)
|
||||||
|
|
||||||
|
_, data = ent1.last_call("turn_off")
|
||||||
|
assert data == {}
|
||||||
|
@ -945,6 +945,7 @@ async def test_sending_hs_color(hass, mqtt_mock):
|
|||||||
"command_topic": "test_light_rgb/set",
|
"command_topic": "test_light_rgb/set",
|
||||||
"brightness": True,
|
"brightness": True,
|
||||||
"hs": True,
|
"hs": True,
|
||||||
|
"white_value": True,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -1139,6 +1140,7 @@ async def test_sending_rgb_color_with_brightness(hass, mqtt_mock):
|
|||||||
"command_topic": "test_light_rgb/set",
|
"command_topic": "test_light_rgb/set",
|
||||||
"brightness": True,
|
"brightness": True,
|
||||||
"rgb": True,
|
"rgb": True,
|
||||||
|
"white_value": True,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -1209,6 +1211,7 @@ async def test_sending_rgb_color_with_scaled_brightness(hass, mqtt_mock):
|
|||||||
"brightness": True,
|
"brightness": True,
|
||||||
"brightness_scale": 100,
|
"brightness_scale": 100,
|
||||||
"rgb": True,
|
"rgb": True,
|
||||||
|
"white_value": True,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -1278,6 +1281,7 @@ async def test_sending_xy_color(hass, mqtt_mock):
|
|||||||
"command_topic": "test_light_rgb/set",
|
"command_topic": "test_light_rgb/set",
|
||||||
"brightness": True,
|
"brightness": True,
|
||||||
"xy": True,
|
"xy": True,
|
||||||
|
"white_value": True,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -117,6 +117,8 @@ async def test_activate_scene(hass, entities, enable_custom_integrations):
|
|||||||
assert light.is_on(hass, light_2.entity_id)
|
assert light.is_on(hass, light_2.entity_id)
|
||||||
assert light_2.last_call("turn_on")[1].get("brightness") == 100
|
assert light_2.last_call("turn_on")[1].get("brightness") == 100
|
||||||
|
|
||||||
|
await turn_off_lights(hass, [light_2.entity_id])
|
||||||
|
|
||||||
calls = async_mock_service(hass, "light", "turn_on")
|
calls = async_mock_service(hass, "light", "turn_on")
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
@ -156,16 +158,22 @@ async def setup_lights(hass, entities):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
light_1, light_2 = entities
|
light_1, light_2 = entities
|
||||||
|
light_1.supported_color_modes = ["brightness"]
|
||||||
|
light_2.supported_color_modes = ["brightness"]
|
||||||
|
|
||||||
await hass.services.async_call(
|
await turn_off_lights(hass, [light_1.entity_id, light_2.entity_id])
|
||||||
"light",
|
|
||||||
"turn_off",
|
|
||||||
{"entity_id": [light_1.entity_id, light_2.entity_id]},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert not light.is_on(hass, light_1.entity_id)
|
assert not light.is_on(hass, light_1.entity_id)
|
||||||
assert not light.is_on(hass, light_2.entity_id)
|
assert not light.is_on(hass, light_2.entity_id)
|
||||||
|
|
||||||
return light_1, light_2
|
return light_1, light_2
|
||||||
|
|
||||||
|
|
||||||
|
async def turn_off_lights(hass, entity_ids):
|
||||||
|
"""Turn lights off."""
|
||||||
|
await hass.services.async_call(
|
||||||
|
"light",
|
||||||
|
"turn_off",
|
||||||
|
{"entity_id": entity_ids},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user