mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add Xiaomi Miio light config flow (#47161)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
b162c45e0a
commit
cf4954fead
@ -18,6 +18,7 @@ from .const import (
|
|||||||
KEY_COORDINATOR,
|
KEY_COORDINATOR,
|
||||||
MODELS_AIR_MONITOR,
|
MODELS_AIR_MONITOR,
|
||||||
MODELS_FAN,
|
MODELS_FAN,
|
||||||
|
MODELS_LIGHT,
|
||||||
MODELS_SWITCH,
|
MODELS_SWITCH,
|
||||||
MODELS_VACUUM,
|
MODELS_VACUUM,
|
||||||
)
|
)
|
||||||
@ -28,6 +29,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor", "switch", "light"]
|
GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor", "switch", "light"]
|
||||||
SWITCH_PLATFORMS = ["switch"]
|
SWITCH_PLATFORMS = ["switch"]
|
||||||
FAN_PLATFORMS = ["fan"]
|
FAN_PLATFORMS = ["fan"]
|
||||||
|
LIGHT_PLATFORMS = ["light"]
|
||||||
VACUUM_PLATFORMS = ["vacuum"]
|
VACUUM_PLATFORMS = ["vacuum"]
|
||||||
AIR_MONITOR_PLATFORMS = ["air_quality", "sensor"]
|
AIR_MONITOR_PLATFORMS = ["air_quality", "sensor"]
|
||||||
|
|
||||||
@ -128,6 +130,8 @@ async def async_setup_device_entry(
|
|||||||
platforms = SWITCH_PLATFORMS
|
platforms = SWITCH_PLATFORMS
|
||||||
elif model in MODELS_FAN:
|
elif model in MODELS_FAN:
|
||||||
platforms = FAN_PLATFORMS
|
platforms = FAN_PLATFORMS
|
||||||
|
elif model in MODELS_LIGHT:
|
||||||
|
platforms = LIGHT_PLATFORMS
|
||||||
for vacuum_model in MODELS_VACUUM:
|
for vacuum_model in MODELS_VACUUM:
|
||||||
if model.startswith(vacuum_model):
|
if model.startswith(vacuum_model):
|
||||||
platforms = VACUUM_PLATFORMS
|
platforms = VACUUM_PLATFORMS
|
||||||
|
@ -9,7 +9,7 @@ CONF_MAC = "mac"
|
|||||||
|
|
||||||
KEY_COORDINATOR = "coordinator"
|
KEY_COORDINATOR = "coordinator"
|
||||||
|
|
||||||
# Fam Models
|
# Fan Models
|
||||||
MODEL_AIRPURIFIER_V1 = "zhimi.airpurifier.v1"
|
MODEL_AIRPURIFIER_V1 = "zhimi.airpurifier.v1"
|
||||||
MODEL_AIRPURIFIER_V2 = "zhimi.airpurifier.v2"
|
MODEL_AIRPURIFIER_V2 = "zhimi.airpurifier.v2"
|
||||||
MODEL_AIRPURIFIER_V3 = "zhimi.airpurifier.v3"
|
MODEL_AIRPURIFIER_V3 = "zhimi.airpurifier.v3"
|
||||||
@ -60,6 +60,18 @@ MODEL_AIRQUALITYMONITOR_V1 = "zhimi.airmonitor.v1"
|
|||||||
MODEL_AIRQUALITYMONITOR_B1 = "cgllc.airmonitor.b1"
|
MODEL_AIRQUALITYMONITOR_B1 = "cgllc.airmonitor.b1"
|
||||||
MODEL_AIRQUALITYMONITOR_S1 = "cgllc.airmonitor.s1"
|
MODEL_AIRQUALITYMONITOR_S1 = "cgllc.airmonitor.s1"
|
||||||
|
|
||||||
|
# Light Models
|
||||||
|
MODELS_LIGHT_EYECARE = ["philips.light.sread1"]
|
||||||
|
MODELS_LIGHT_CEILING = ["philips.light.ceiling", "philips.light.zyceiling"]
|
||||||
|
MODELS_LIGHT_MOON = ["philips.light.moonlight"]
|
||||||
|
MODELS_LIGHT_BULB = [
|
||||||
|
"philips.light.bulb",
|
||||||
|
"philips.light.candle",
|
||||||
|
"philips.light.candle2",
|
||||||
|
"philips.light.downlight",
|
||||||
|
]
|
||||||
|
MODELS_LIGHT_MONO = ["philips.light.mono1"]
|
||||||
|
|
||||||
# Model lists
|
# Model lists
|
||||||
MODELS_GATEWAY = ["lumi.gateway", "lumi.acpartner"]
|
MODELS_GATEWAY = ["lumi.gateway", "lumi.acpartner"]
|
||||||
MODELS_SWITCH = [
|
MODELS_SWITCH = [
|
||||||
@ -75,6 +87,13 @@ MODELS_SWITCH = [
|
|||||||
"chuangmi.plug.hmi206",
|
"chuangmi.plug.hmi206",
|
||||||
]
|
]
|
||||||
MODELS_FAN = MODELS_FAN_MIIO + MODELS_HUMIDIFIER_MIOT + MODELS_PURIFIER_MIOT
|
MODELS_FAN = MODELS_FAN_MIIO + MODELS_HUMIDIFIER_MIOT + MODELS_PURIFIER_MIOT
|
||||||
|
MODELS_LIGHT = (
|
||||||
|
MODELS_LIGHT_EYECARE
|
||||||
|
+ MODELS_LIGHT_CEILING
|
||||||
|
+ MODELS_LIGHT_MOON
|
||||||
|
+ MODELS_LIGHT_BULB
|
||||||
|
+ MODELS_LIGHT_MONO
|
||||||
|
)
|
||||||
MODELS_VACUUM = ["roborock.vacuum", "rockrobo.vacuum"]
|
MODELS_VACUUM = ["roborock.vacuum", "rockrobo.vacuum"]
|
||||||
MODELS_AIR_MONITOR = [
|
MODELS_AIR_MONITOR = [
|
||||||
MODEL_AIRQUALITYMONITOR_V1,
|
MODEL_AIRQUALITYMONITOR_V1,
|
||||||
@ -82,7 +101,9 @@ MODELS_AIR_MONITOR = [
|
|||||||
MODEL_AIRQUALITYMONITOR_S1,
|
MODEL_AIRQUALITYMONITOR_S1,
|
||||||
]
|
]
|
||||||
|
|
||||||
MODELS_ALL_DEVICES = MODELS_SWITCH + MODELS_VACUUM + MODELS_AIR_MONITOR + MODELS_FAN
|
MODELS_ALL_DEVICES = (
|
||||||
|
MODELS_SWITCH + MODELS_VACUUM + MODELS_AIR_MONITOR + MODELS_FAN + MODELS_LIGHT
|
||||||
|
)
|
||||||
MODELS_ALL = MODELS_ALL_DEVICES + MODELS_GATEWAY
|
MODELS_ALL = MODELS_ALL_DEVICES + MODELS_GATEWAY
|
||||||
|
|
||||||
# Fan Services
|
# Fan Services
|
||||||
|
@ -6,14 +6,7 @@ from functools import partial
|
|||||||
import logging
|
import logging
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
|
||||||
from miio import (
|
from miio import Ceil, DeviceException, PhilipsBulb, PhilipsEyecare, PhilipsMoonlight
|
||||||
Ceil,
|
|
||||||
Device,
|
|
||||||
DeviceException,
|
|
||||||
PhilipsBulb,
|
|
||||||
PhilipsEyecare,
|
|
||||||
PhilipsMoonlight,
|
|
||||||
)
|
|
||||||
from miio.gateway import (
|
from miio.gateway import (
|
||||||
GATEWAY_MODEL_AC_V1,
|
GATEWAY_MODEL_AC_V1,
|
||||||
GATEWAY_MODEL_AC_V2,
|
GATEWAY_MODEL_AC_V2,
|
||||||
@ -32,15 +25,23 @@ from homeassistant.components.light import (
|
|||||||
SUPPORT_COLOR_TEMP,
|
SUPPORT_COLOR_TEMP,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import SOURCE_IMPORT
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import color, dt
|
from homeassistant.util import color, dt
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CONF_DEVICE,
|
||||||
CONF_FLOW_TYPE,
|
CONF_FLOW_TYPE,
|
||||||
CONF_GATEWAY,
|
CONF_GATEWAY,
|
||||||
|
CONF_MODEL,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
MODELS_LIGHT,
|
||||||
|
MODELS_LIGHT_BULB,
|
||||||
|
MODELS_LIGHT_CEILING,
|
||||||
|
MODELS_LIGHT_EYECARE,
|
||||||
|
MODELS_LIGHT_MONO,
|
||||||
|
MODELS_LIGHT_MOON,
|
||||||
SERVICE_EYECARE_MODE_OFF,
|
SERVICE_EYECARE_MODE_OFF,
|
||||||
SERVICE_EYECARE_MODE_ON,
|
SERVICE_EYECARE_MODE_ON,
|
||||||
SERVICE_NIGHT_LIGHT_MODE_OFF,
|
SERVICE_NIGHT_LIGHT_MODE_OFF,
|
||||||
@ -50,32 +51,19 @@ from .const import (
|
|||||||
SERVICE_SET_DELAYED_TURN_OFF,
|
SERVICE_SET_DELAYED_TURN_OFF,
|
||||||
SERVICE_SET_SCENE,
|
SERVICE_SET_SCENE,
|
||||||
)
|
)
|
||||||
|
from .device import XiaomiMiioEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_NAME = "Xiaomi Philips Light"
|
DEFAULT_NAME = "Xiaomi Philips Light"
|
||||||
DATA_KEY = "light.xiaomi_miio"
|
DATA_KEY = "light.xiaomi_miio"
|
||||||
|
|
||||||
CONF_MODEL = "model"
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
vol.Required(CONF_TOKEN): vol.All(cv.string, vol.Length(min=32, max=32)),
|
vol.Required(CONF_TOKEN): vol.All(cv.string, vol.Length(min=32, max=32)),
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_MODEL): vol.In(
|
vol.Optional(CONF_MODEL): vol.In(MODELS_LIGHT),
|
||||||
[
|
|
||||||
"philips.light.sread1",
|
|
||||||
"philips.light.ceiling",
|
|
||||||
"philips.light.zyceiling",
|
|
||||||
"philips.light.moonlight",
|
|
||||||
"philips.light.bulb",
|
|
||||||
"philips.light.candle",
|
|
||||||
"philips.light.candle2",
|
|
||||||
"philips.light.mono1",
|
|
||||||
"philips.light.downlight",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,7 +75,6 @@ DELAYED_TURN_OFF_MAX_DEVIATION_SECONDS = 4
|
|||||||
DELAYED_TURN_OFF_MAX_DEVIATION_MINUTES = 1
|
DELAYED_TURN_OFF_MAX_DEVIATION_MINUTES = 1
|
||||||
|
|
||||||
SUCCESS = ["ok"]
|
SUCCESS = ["ok"]
|
||||||
ATTR_MODEL = "model"
|
|
||||||
ATTR_SCENE = "scene"
|
ATTR_SCENE = "scene"
|
||||||
ATTR_DELAYED_TURN_OFF = "delayed_turn_off"
|
ATTR_DELAYED_TURN_OFF = "delayed_turn_off"
|
||||||
ATTR_TIME_PERIOD = "time_period"
|
ATTR_TIME_PERIOD = "time_period"
|
||||||
@ -100,8 +87,8 @@ ATTR_EYECARE_MODE = "eyecare_mode"
|
|||||||
ATTR_SLEEP_ASSISTANT = "sleep_assistant"
|
ATTR_SLEEP_ASSISTANT = "sleep_assistant"
|
||||||
ATTR_SLEEP_OFF_TIME = "sleep_off_time"
|
ATTR_SLEEP_OFF_TIME = "sleep_off_time"
|
||||||
ATTR_TOTAL_ASSISTANT_SLEEP_TIME = "total_assistant_sleep_time"
|
ATTR_TOTAL_ASSISTANT_SLEEP_TIME = "total_assistant_sleep_time"
|
||||||
ATTR_BRAND_SLEEP = "brand_sleep"
|
ATTR_BAND_SLEEP = "band_sleep"
|
||||||
ATTR_BRAND = "brand"
|
ATTR_BAND = "band"
|
||||||
|
|
||||||
XIAOMI_MIIO_SERVICE_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids})
|
XIAOMI_MIIO_SERVICE_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids})
|
||||||
|
|
||||||
@ -131,6 +118,21 @@ SERVICE_TO_METHOD = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
|
"""Import Miio configuration from YAML."""
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Loading Xiaomi Miio Light via platform setup is deprecated. "
|
||||||
|
"Please remove it from your configuration"
|
||||||
|
)
|
||||||
|
hass.async_create_task(
|
||||||
|
hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_IMPORT},
|
||||||
|
data=config,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the Xiaomi light from a config entry."""
|
"""Set up the Xiaomi light from a config entry."""
|
||||||
entities = []
|
entities = []
|
||||||
@ -147,76 +149,51 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
XiaomiGatewayLight(gateway, config_entry.title, config_entry.unique_id)
|
XiaomiGatewayLight(gateway, config_entry.title, config_entry.unique_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(entities, update_before_add=True)
|
if config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
|
||||||
"""Set up the light from config."""
|
|
||||||
if DATA_KEY not in hass.data:
|
if DATA_KEY not in hass.data:
|
||||||
hass.data[DATA_KEY] = {}
|
hass.data[DATA_KEY] = {}
|
||||||
|
|
||||||
host = config[CONF_HOST]
|
host = config_entry.data[CONF_HOST]
|
||||||
token = config[CONF_TOKEN]
|
token = config_entry.data[CONF_TOKEN]
|
||||||
name = config[CONF_NAME]
|
name = config_entry.title
|
||||||
model = config.get(CONF_MODEL)
|
model = config_entry.data[CONF_MODEL]
|
||||||
|
unique_id = config_entry.unique_id
|
||||||
|
|
||||||
_LOGGER.info("Initializing with host %s (token %s...)", host, token[:5])
|
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
|
||||||
|
|
||||||
devices = []
|
if model in MODELS_LIGHT_EYECARE:
|
||||||
unique_id = None
|
|
||||||
|
|
||||||
if model is None:
|
|
||||||
try:
|
|
||||||
miio_device = Device(host, token)
|
|
||||||
device_info = await hass.async_add_executor_job(miio_device.info)
|
|
||||||
model = device_info.model
|
|
||||||
unique_id = f"{model}-{device_info.mac_address}"
|
|
||||||
_LOGGER.info(
|
|
||||||
"%s %s %s detected",
|
|
||||||
model,
|
|
||||||
device_info.firmware_version,
|
|
||||||
device_info.hardware_version,
|
|
||||||
)
|
|
||||||
except DeviceException as ex:
|
|
||||||
raise PlatformNotReady from ex
|
|
||||||
|
|
||||||
if model == "philips.light.sread1":
|
|
||||||
light = PhilipsEyecare(host, token)
|
light = PhilipsEyecare(host, token)
|
||||||
primary_device = XiaomiPhilipsEyecareLamp(name, light, model, unique_id)
|
entity = XiaomiPhilipsEyecareLamp(name, light, config_entry, unique_id)
|
||||||
devices.append(primary_device)
|
entities.append(entity)
|
||||||
hass.data[DATA_KEY][host] = primary_device
|
hass.data[DATA_KEY][host] = entity
|
||||||
|
|
||||||
secondary_device = XiaomiPhilipsEyecareLampAmbientLight(
|
entities.append(
|
||||||
name, light, model, unique_id
|
XiaomiPhilipsEyecareLampAmbientLight(
|
||||||
|
name, light, config_entry, unique_id
|
||||||
|
)
|
||||||
)
|
)
|
||||||
devices.append(secondary_device)
|
|
||||||
# The ambient light doesn't expose additional services.
|
# The ambient light doesn't expose additional services.
|
||||||
# A hass.data[DATA_KEY] entry isn't needed.
|
# A hass.data[DATA_KEY] entry isn't needed.
|
||||||
elif model in ["philips.light.ceiling", "philips.light.zyceiling"]:
|
elif model in MODELS_LIGHT_CEILING:
|
||||||
light = Ceil(host, token)
|
light = Ceil(host, token)
|
||||||
device = XiaomiPhilipsCeilingLamp(name, light, model, unique_id)
|
entity = XiaomiPhilipsCeilingLamp(name, light, config_entry, unique_id)
|
||||||
devices.append(device)
|
entities.append(entity)
|
||||||
hass.data[DATA_KEY][host] = device
|
hass.data[DATA_KEY][host] = entity
|
||||||
elif model == "philips.light.moonlight":
|
elif model in MODELS_LIGHT_MOON:
|
||||||
light = PhilipsMoonlight(host, token)
|
light = PhilipsMoonlight(host, token)
|
||||||
device = XiaomiPhilipsMoonlightLamp(name, light, model, unique_id)
|
entity = XiaomiPhilipsMoonlightLamp(name, light, config_entry, unique_id)
|
||||||
devices.append(device)
|
entities.append(entity)
|
||||||
hass.data[DATA_KEY][host] = device
|
hass.data[DATA_KEY][host] = entity
|
||||||
elif model in [
|
elif model in MODELS_LIGHT_BULB:
|
||||||
"philips.light.bulb",
|
|
||||||
"philips.light.candle",
|
|
||||||
"philips.light.candle2",
|
|
||||||
"philips.light.downlight",
|
|
||||||
]:
|
|
||||||
light = PhilipsBulb(host, token)
|
light = PhilipsBulb(host, token)
|
||||||
device = XiaomiPhilipsBulb(name, light, model, unique_id)
|
entity = XiaomiPhilipsBulb(name, light, config_entry, unique_id)
|
||||||
devices.append(device)
|
entities.append(entity)
|
||||||
hass.data[DATA_KEY][host] = device
|
hass.data[DATA_KEY][host] = entity
|
||||||
elif model == "philips.light.mono1":
|
elif model in MODELS_LIGHT_MONO:
|
||||||
light = PhilipsBulb(host, token)
|
light = PhilipsBulb(host, token)
|
||||||
device = XiaomiPhilipsGenericLight(name, light, model, unique_id)
|
entity = XiaomiPhilipsGenericLight(name, light, config_entry, unique_id)
|
||||||
devices.append(device)
|
entities.append(entity)
|
||||||
hass.data[DATA_KEY][host] = device
|
hass.data[DATA_KEY][host] = entity
|
||||||
else:
|
else:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Unsupported device found! Please create an issue at "
|
"Unsupported device found! Please create an issue at "
|
||||||
@ -224,15 +201,15 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
"and provide the following data: %s",
|
"and provide the following data: %s",
|
||||||
model,
|
model,
|
||||||
)
|
)
|
||||||
return False
|
return
|
||||||
|
|
||||||
async_add_entities(devices, update_before_add=True)
|
|
||||||
|
|
||||||
async def async_service_handler(service):
|
async def async_service_handler(service):
|
||||||
"""Map services to methods on Xiaomi Philips Lights."""
|
"""Map services to methods on Xiaomi Philips Lights."""
|
||||||
method = SERVICE_TO_METHOD.get(service.service)
|
method = SERVICE_TO_METHOD.get(service.service)
|
||||||
params = {
|
params = {
|
||||||
key: value for key, value in service.data.items() if key != ATTR_ENTITY_ID
|
key: value
|
||||||
|
for key, value in service.data.items()
|
||||||
|
if key != ATTR_ENTITY_ID
|
||||||
}
|
}
|
||||||
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
||||||
if entity_ids:
|
if entity_ids:
|
||||||
@ -262,32 +239,20 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
DOMAIN, xiaomi_miio_service, async_service_handler, schema=schema
|
DOMAIN, xiaomi_miio_service, async_service_handler, schema=schema
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async_add_entities(entities, update_before_add=True)
|
||||||
|
|
||||||
class XiaomiPhilipsAbstractLight(LightEntity):
|
|
||||||
|
class XiaomiPhilipsAbstractLight(XiaomiMiioEntity, LightEntity):
|
||||||
"""Representation of a Abstract Xiaomi Philips Light."""
|
"""Representation of a Abstract Xiaomi Philips Light."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
self._name = name
|
super().__init__(name, device, entry, unique_id)
|
||||||
self._light = light
|
|
||||||
self._model = model
|
|
||||||
self._unique_id = unique_id
|
|
||||||
|
|
||||||
self._brightness = None
|
self._brightness = None
|
||||||
|
|
||||||
self._available = False
|
self._available = False
|
||||||
self._state = None
|
self._state = None
|
||||||
self._state_attrs = {ATTR_MODEL: self._model}
|
self._state_attrs = {}
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self):
|
|
||||||
"""Return an unique ID."""
|
|
||||||
return self._unique_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the device if any."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
@ -341,23 +306,23 @@ class XiaomiPhilipsAbstractLight(LightEntity):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness failed: %s",
|
"Setting brightness failed: %s",
|
||||||
self._light.set_brightness,
|
self._device.set_brightness,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
)
|
)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
self._brightness = brightness
|
self._brightness = brightness
|
||||||
else:
|
else:
|
||||||
await self._try_command("Turning the light on failed.", self._light.on)
|
await self._try_command("Turning the light on failed.", self._device.on)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs):
|
||||||
"""Turn the light off."""
|
"""Turn the light off."""
|
||||||
await self._try_command("Turning the light off failed.", self._light.off)
|
await self._try_command("Turning the light off failed.", self._device.off)
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -374,16 +339,16 @@ class XiaomiPhilipsAbstractLight(LightEntity):
|
|||||||
class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
||||||
"""Representation of a Generic Xiaomi Philips Light."""
|
"""Representation of a Generic Xiaomi Philips Light."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
super().__init__(name, light, model, unique_id)
|
super().__init__(name, device, entry, unique_id)
|
||||||
|
|
||||||
self._state_attrs.update({ATTR_SCENE: None, ATTR_DELAYED_TURN_OFF: None})
|
self._state_attrs.update({ATTR_SCENE: None, ATTR_DELAYED_TURN_OFF: None})
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -409,14 +374,14 @@ class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
|||||||
async def async_set_scene(self, scene: int = 1):
|
async def async_set_scene(self, scene: int = 1):
|
||||||
"""Set the fixed scene."""
|
"""Set the fixed scene."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Setting a fixed scene failed.", self._light.set_scene, scene
|
"Setting a fixed scene failed.", self._device.set_scene, scene
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_set_delayed_turn_off(self, time_period: timedelta):
|
async def async_set_delayed_turn_off(self, time_period: timedelta):
|
||||||
"""Set delayed turn off."""
|
"""Set delayed turn off."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Setting the turn off delay failed.",
|
"Setting the turn off delay failed.",
|
||||||
self._light.delay_off,
|
self._device.delay_off,
|
||||||
time_period.total_seconds(),
|
time_period.total_seconds(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -445,9 +410,9 @@ class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
|||||||
class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
||||||
"""Representation of a Xiaomi Philips Bulb."""
|
"""Representation of a Xiaomi Philips Bulb."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
super().__init__(name, light, model, unique_id)
|
super().__init__(name, device, entry, unique_id)
|
||||||
|
|
||||||
self._color_temp = None
|
self._color_temp = None
|
||||||
|
|
||||||
@ -495,7 +460,7 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness and color temperature failed: %s bri, %s cct",
|
"Setting brightness and color temperature failed: %s bri, %s cct",
|
||||||
self._light.set_brightness_and_color_temperature,
|
self._device.set_brightness_and_color_temperature,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
percent_color_temp,
|
percent_color_temp,
|
||||||
)
|
)
|
||||||
@ -513,7 +478,7 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting color temperature failed: %s cct",
|
"Setting color temperature failed: %s cct",
|
||||||
self._light.set_color_temperature,
|
self._device.set_color_temperature,
|
||||||
percent_color_temp,
|
percent_color_temp,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -528,7 +493,7 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness failed: %s",
|
"Setting brightness failed: %s",
|
||||||
self._light.set_brightness,
|
self._device.set_brightness,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -536,12 +501,12 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
|||||||
self._brightness = brightness
|
self._brightness = brightness
|
||||||
|
|
||||||
else:
|
else:
|
||||||
await self._try_command("Turning the light on failed.", self._light.on)
|
await self._try_command("Turning the light on failed.", self._device.on)
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -579,9 +544,9 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
|||||||
class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb):
|
class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb):
|
||||||
"""Representation of a Xiaomi Philips Ceiling Lamp."""
|
"""Representation of a Xiaomi Philips Ceiling Lamp."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
super().__init__(name, light, model, unique_id)
|
super().__init__(name, device, entry, unique_id)
|
||||||
|
|
||||||
self._state_attrs.update(
|
self._state_attrs.update(
|
||||||
{ATTR_NIGHT_LIGHT_MODE: None, ATTR_AUTOMATIC_COLOR_TEMPERATURE: None}
|
{ATTR_NIGHT_LIGHT_MODE: None, ATTR_AUTOMATIC_COLOR_TEMPERATURE: None}
|
||||||
@ -600,7 +565,7 @@ class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb):
|
|||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -635,9 +600,9 @@ class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb):
|
|||||||
class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
||||||
"""Representation of a Xiaomi Philips Eyecare Lamp 2."""
|
"""Representation of a Xiaomi Philips Eyecare Lamp 2."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
super().__init__(name, light, model, unique_id)
|
super().__init__(name, device, entry, unique_id)
|
||||||
|
|
||||||
self._state_attrs.update(
|
self._state_attrs.update(
|
||||||
{ATTR_REMINDER: None, ATTR_NIGHT_LIGHT_MODE: None, ATTR_EYECARE_MODE: None}
|
{ATTR_REMINDER: None, ATTR_NIGHT_LIGHT_MODE: None, ATTR_EYECARE_MODE: None}
|
||||||
@ -646,7 +611,7 @@ class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
|||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -679,46 +644,46 @@ class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
|||||||
"""Set delayed turn off."""
|
"""Set delayed turn off."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Setting the turn off delay failed.",
|
"Setting the turn off delay failed.",
|
||||||
self._light.delay_off,
|
self._device.delay_off,
|
||||||
round(time_period.total_seconds() / 60),
|
round(time_period.total_seconds() / 60),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_reminder_on(self):
|
async def async_reminder_on(self):
|
||||||
"""Enable the eye fatigue notification."""
|
"""Enable the eye fatigue notification."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning on the reminder failed.", self._light.reminder_on
|
"Turning on the reminder failed.", self._device.reminder_on
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_reminder_off(self):
|
async def async_reminder_off(self):
|
||||||
"""Disable the eye fatigue notification."""
|
"""Disable the eye fatigue notification."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning off the reminder failed.", self._light.reminder_off
|
"Turning off the reminder failed.", self._device.reminder_off
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_night_light_mode_on(self):
|
async def async_night_light_mode_on(self):
|
||||||
"""Turn the smart night light mode on."""
|
"""Turn the smart night light mode on."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning on the smart night light mode failed.",
|
"Turning on the smart night light mode failed.",
|
||||||
self._light.smart_night_light_on,
|
self._device.smart_night_light_on,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_night_light_mode_off(self):
|
async def async_night_light_mode_off(self):
|
||||||
"""Turn the smart night light mode off."""
|
"""Turn the smart night light mode off."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning off the smart night light mode failed.",
|
"Turning off the smart night light mode failed.",
|
||||||
self._light.smart_night_light_off,
|
self._device.smart_night_light_off,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_eyecare_mode_on(self):
|
async def async_eyecare_mode_on(self):
|
||||||
"""Turn the eyecare mode on."""
|
"""Turn the eyecare mode on."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning on the eyecare mode failed.", self._light.eyecare_on
|
"Turning on the eyecare mode failed.", self._device.eyecare_on
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_eyecare_mode_off(self):
|
async def async_eyecare_mode_off(self):
|
||||||
"""Turn the eyecare mode off."""
|
"""Turn the eyecare mode off."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning off the eyecare mode failed.", self._light.eyecare_off
|
"Turning off the eyecare mode failed.", self._device.eyecare_off
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -748,12 +713,12 @@ class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
|||||||
class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
|
class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
|
||||||
"""Representation of a Xiaomi Philips Eyecare Lamp Ambient Light."""
|
"""Representation of a Xiaomi Philips Eyecare Lamp Ambient Light."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
name = f"{name} Ambient Light"
|
name = f"{name} Ambient Light"
|
||||||
if unique_id is not None:
|
if unique_id is not None:
|
||||||
unique_id = f"{unique_id}-ambient"
|
unique_id = f"{unique_id}-ambient"
|
||||||
super().__init__(name, light, model, unique_id)
|
super().__init__(name, device, entry, unique_id)
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn the light on."""
|
"""Turn the light on."""
|
||||||
@ -769,7 +734,7 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness of the ambient failed: %s",
|
"Setting brightness of the ambient failed: %s",
|
||||||
self._light.set_ambient_brightness,
|
self._device.set_ambient_brightness,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -777,19 +742,19 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
|
|||||||
self._brightness = brightness
|
self._brightness = brightness
|
||||||
else:
|
else:
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning the ambient light on failed.", self._light.ambient_on
|
"Turning the ambient light on failed.", self._device.ambient_on
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs):
|
||||||
"""Turn the light off."""
|
"""Turn the light off."""
|
||||||
await self._try_command(
|
await self._try_command(
|
||||||
"Turning the ambient light off failed.", self._light.ambient_off
|
"Turning the ambient light off failed.", self._device.ambient_off
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -806,9 +771,9 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
|
|||||||
class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
||||||
"""Representation of a Xiaomi Philips Zhirui Bedside Lamp."""
|
"""Representation of a Xiaomi Philips Zhirui Bedside Lamp."""
|
||||||
|
|
||||||
def __init__(self, name, light, model, unique_id):
|
def __init__(self, name, device, entry, unique_id):
|
||||||
"""Initialize the light device."""
|
"""Initialize the light device."""
|
||||||
super().__init__(name, light, model, unique_id)
|
super().__init__(name, device, entry, unique_id)
|
||||||
|
|
||||||
self._hs_color = None
|
self._hs_color = None
|
||||||
self._state_attrs.pop(ATTR_DELAYED_TURN_OFF)
|
self._state_attrs.pop(ATTR_DELAYED_TURN_OFF)
|
||||||
@ -817,8 +782,8 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
ATTR_SLEEP_ASSISTANT: None,
|
ATTR_SLEEP_ASSISTANT: None,
|
||||||
ATTR_SLEEP_OFF_TIME: None,
|
ATTR_SLEEP_OFF_TIME: None,
|
||||||
ATTR_TOTAL_ASSISTANT_SLEEP_TIME: None,
|
ATTR_TOTAL_ASSISTANT_SLEEP_TIME: None,
|
||||||
ATTR_BRAND_SLEEP: None,
|
ATTR_BAND_SLEEP: None,
|
||||||
ATTR_BRAND: None,
|
ATTR_BAND: None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -868,7 +833,7 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness and color failed: %s bri, %s color",
|
"Setting brightness and color failed: %s bri, %s color",
|
||||||
self._light.set_brightness_and_rgb,
|
self._device.set_brightness_and_rgb,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
rgb,
|
rgb,
|
||||||
)
|
)
|
||||||
@ -889,7 +854,7 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness and color temperature failed: %s bri, %s cct",
|
"Setting brightness and color temperature failed: %s bri, %s cct",
|
||||||
self._light.set_brightness_and_color_temperature,
|
self._device.set_brightness_and_color_temperature,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
percent_color_temp,
|
percent_color_temp,
|
||||||
)
|
)
|
||||||
@ -902,7 +867,7 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
_LOGGER.debug("Setting color: %s", rgb)
|
_LOGGER.debug("Setting color: %s", rgb)
|
||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting color failed: %s", self._light.set_rgb, rgb
|
"Setting color failed: %s", self._device.set_rgb, rgb
|
||||||
)
|
)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
@ -917,7 +882,7 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting color temperature failed: %s cct",
|
"Setting color temperature failed: %s cct",
|
||||||
self._light.set_color_temperature,
|
self._device.set_color_temperature,
|
||||||
percent_color_temp,
|
percent_color_temp,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -932,7 +897,7 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
|
|
||||||
result = await self._try_command(
|
result = await self._try_command(
|
||||||
"Setting brightness failed: %s",
|
"Setting brightness failed: %s",
|
||||||
self._light.set_brightness,
|
self._device.set_brightness,
|
||||||
percent_brightness,
|
percent_brightness,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -940,12 +905,12 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
self._brightness = brightness
|
self._brightness = brightness
|
||||||
|
|
||||||
else:
|
else:
|
||||||
await self._try_command("Turning the light on failed.", self._light.on)
|
await self._try_command("Turning the light on failed.", self._device.on)
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._device.status)
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
if self._available:
|
if self._available:
|
||||||
self._available = False
|
self._available = False
|
||||||
@ -968,8 +933,8 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||||||
ATTR_SLEEP_ASSISTANT: state.sleep_assistant,
|
ATTR_SLEEP_ASSISTANT: state.sleep_assistant,
|
||||||
ATTR_SLEEP_OFF_TIME: state.sleep_off_time,
|
ATTR_SLEEP_OFF_TIME: state.sleep_off_time,
|
||||||
ATTR_TOTAL_ASSISTANT_SLEEP_TIME: state.total_assistant_sleep_time,
|
ATTR_TOTAL_ASSISTANT_SLEEP_TIME: state.total_assistant_sleep_time,
|
||||||
ATTR_BRAND_SLEEP: state.brand_sleep,
|
ATTR_BAND_SLEEP: state.brand_sleep,
|
||||||
ATTR_BRAND: state.brand,
|
ATTR_BAND: state.brand,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user