mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Add Freedompro (#46332)
Co-authored-by: Milan Meulemans <milan.meulemans@live.be> Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
parent
dc407fe7a1
commit
d8337cf98f
@ -169,6 +169,7 @@ homeassistant/components/forked_daapd/* @uvjustin
|
|||||||
homeassistant/components/fortios/* @kimfrellsen
|
homeassistant/components/fortios/* @kimfrellsen
|
||||||
homeassistant/components/foscam/* @skgsergio
|
homeassistant/components/foscam/* @skgsergio
|
||||||
homeassistant/components/freebox/* @hacf-fr @Quentame
|
homeassistant/components/freebox/* @hacf-fr @Quentame
|
||||||
|
homeassistant/components/freedompro/* @stefano055415
|
||||||
homeassistant/components/fritz/* @mammuth @AaronDavidSchneider @chemelli74
|
homeassistant/components/fritz/* @mammuth @AaronDavidSchneider @chemelli74
|
||||||
homeassistant/components/fritzbox/* @mib1185
|
homeassistant/components/fritzbox/* @mib1185
|
||||||
homeassistant/components/fronius/* @nielstron
|
homeassistant/components/fronius/* @nielstron
|
||||||
|
84
homeassistant/components/freedompro/__init__.py
Normal file
84
homeassistant/components/freedompro/__init__.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""Support for freedompro."""
|
||||||
|
from datetime import timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from pyfreedompro import get_list, get_states
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_API_KEY
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
PLATFORMS = ["light"]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Set up Freedompro from a config entry."""
|
||||||
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
api_key = entry.data[CONF_API_KEY]
|
||||||
|
|
||||||
|
coordinator = FreedomproDataUpdateCoordinator(hass, api_key)
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||||
|
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Unload a config entry."""
|
||||||
|
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
if unload_ok:
|
||||||
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
|
async def update_listener(hass, config_entry):
|
||||||
|
"""Update listener."""
|
||||||
|
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
class FreedomproDataUpdateCoordinator(DataUpdateCoordinator):
|
||||||
|
"""Class to manage fetching Freedompro data API."""
|
||||||
|
|
||||||
|
def __init__(self, hass, api_key):
|
||||||
|
"""Initialize."""
|
||||||
|
self._hass = hass
|
||||||
|
self._api_key = api_key
|
||||||
|
self._devices = None
|
||||||
|
|
||||||
|
update_interval = timedelta(minutes=1)
|
||||||
|
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
|
||||||
|
|
||||||
|
async def _async_update_data(self):
|
||||||
|
if self._devices is None:
|
||||||
|
result = await get_list(
|
||||||
|
aiohttp_client.async_get_clientsession(self._hass), self._api_key
|
||||||
|
)
|
||||||
|
if result["state"]:
|
||||||
|
self._devices = result["devices"]
|
||||||
|
else:
|
||||||
|
raise UpdateFailed()
|
||||||
|
|
||||||
|
result = await get_states(
|
||||||
|
aiohttp_client.async_get_clientsession(self._hass), self._api_key
|
||||||
|
)
|
||||||
|
|
||||||
|
for device in self._devices:
|
||||||
|
dev = next(
|
||||||
|
(dev for dev in result if dev["uid"] == device["uid"]),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if dev is not None and "state" in dev:
|
||||||
|
device["state"] = dev["state"]
|
||||||
|
return self._devices
|
73
homeassistant/components/freedompro/config_flow.py
Normal file
73
homeassistant/components/freedompro/config_flow.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
"""Config flow to configure Freedompro."""
|
||||||
|
from pyfreedompro import get_list
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries, core, exceptions
|
||||||
|
from homeassistant.const import CONF_API_KEY
|
||||||
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): str})
|
||||||
|
|
||||||
|
|
||||||
|
class Hub:
|
||||||
|
"""Freedompro Hub class."""
|
||||||
|
|
||||||
|
def __init__(self, hass, api_key):
|
||||||
|
"""Freedompro Hub class init."""
|
||||||
|
self._hass = hass
|
||||||
|
self._api_key = api_key
|
||||||
|
|
||||||
|
async def authenticate(self):
|
||||||
|
"""Freedompro Hub class authenticate."""
|
||||||
|
return await get_list(
|
||||||
|
aiohttp_client.async_get_clientsession(self._hass), self._api_key
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def validate_input(hass: core.HomeAssistant, api_key):
|
||||||
|
"""Validate api key."""
|
||||||
|
hub = Hub(hass, api_key)
|
||||||
|
result = await hub.authenticate()
|
||||||
|
if result["state"] is False:
|
||||||
|
if result["code"] == -201:
|
||||||
|
raise InvalidAuth
|
||||||
|
if result["code"] == -200:
|
||||||
|
raise CannotConnect
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Show the setup form to the user."""
|
||||||
|
if user_input is None:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
||||||
|
)
|
||||||
|
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
await validate_input(self.hass, user_input[CONF_API_KEY])
|
||||||
|
except CannotConnect:
|
||||||
|
errors["base"] = "cannot_connect"
|
||||||
|
except InvalidAuth:
|
||||||
|
errors["base"] = "invalid_auth"
|
||||||
|
else:
|
||||||
|
return self.async_create_entry(title="Freedompro", data=user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CannotConnect(exceptions.HomeAssistantError):
|
||||||
|
"""Error to indicate we cannot connect."""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidAuth(exceptions.HomeAssistantError):
|
||||||
|
"""Error to indicate there is invalid auth."""
|
3
homeassistant/components/freedompro/const.py
Normal file
3
homeassistant/components/freedompro/const.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"""Constants for the Freedompro integration."""
|
||||||
|
|
||||||
|
DOMAIN = "freedompro"
|
109
homeassistant/components/freedompro/light.py
Normal file
109
homeassistant/components/freedompro/light.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""Support for Freedompro light."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
from pyfreedompro import put_state
|
||||||
|
|
||||||
|
from homeassistant.components.light import (
|
||||||
|
ATTR_BRIGHTNESS,
|
||||||
|
ATTR_HS_COLOR,
|
||||||
|
COLOR_MODE_BRIGHTNESS,
|
||||||
|
COLOR_MODE_HS,
|
||||||
|
COLOR_MODE_ONOFF,
|
||||||
|
LightEntity,
|
||||||
|
)
|
||||||
|
from homeassistant.const import CONF_API_KEY
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers import aiohttp_client
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass, entry, async_add_entities):
|
||||||
|
"""Set up Freedompro light."""
|
||||||
|
api_key = entry.data[CONF_API_KEY]
|
||||||
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_entities(
|
||||||
|
Device(hass, api_key, device, coordinator)
|
||||||
|
for device in coordinator.data
|
||||||
|
if device["type"] == "lightbulb"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Device(CoordinatorEntity, LightEntity):
|
||||||
|
"""Representation of an Freedompro light."""
|
||||||
|
|
||||||
|
def __init__(self, hass, api_key, device, coordinator):
|
||||||
|
"""Initialize the Freedompro light."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self._hass = hass
|
||||||
|
self._session = aiohttp_client.async_get_clientsession(self._hass)
|
||||||
|
self._api_key = api_key
|
||||||
|
self._attr_name = device["name"]
|
||||||
|
self._attr_unique_id = device["uid"]
|
||||||
|
self._type = device["type"]
|
||||||
|
self._characteristics = device["characteristics"]
|
||||||
|
self._attr_is_on = False
|
||||||
|
self._attr_brightness = 0
|
||||||
|
color_mode = COLOR_MODE_ONOFF
|
||||||
|
if "hue" in self._characteristics:
|
||||||
|
color_mode = COLOR_MODE_HS
|
||||||
|
elif "brightness" in self._characteristics:
|
||||||
|
color_mode = COLOR_MODE_BRIGHTNESS
|
||||||
|
self._attr_color_mode = color_mode
|
||||||
|
self._attr_supported_color_modes = {color_mode}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_coordinator_update(self) -> None:
|
||||||
|
"""Handle updated data from the coordinator."""
|
||||||
|
device = next(
|
||||||
|
(
|
||||||
|
device
|
||||||
|
for device in self.coordinator.data
|
||||||
|
if device["uid"] == self._attr_unique_id
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if device is not None and "state" in device:
|
||||||
|
state = device["state"]
|
||||||
|
if "on" in state:
|
||||||
|
self._attr_is_on = state["on"]
|
||||||
|
if "brightness" in state:
|
||||||
|
self._attr_brightness = round(state["brightness"] / 100 * 255)
|
||||||
|
if "hue" in state and "saturation" in state:
|
||||||
|
self._attr_hs_color = (state["hue"], state["saturation"])
|
||||||
|
super()._handle_coordinator_update()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""When entity is added to hass."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
self._handle_coordinator_update()
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs):
|
||||||
|
"""Async function to set on to light."""
|
||||||
|
payload = {"on": True}
|
||||||
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
payload["brightness"] = round(kwargs[ATTR_BRIGHTNESS] / 255 * 100)
|
||||||
|
if ATTR_HS_COLOR in kwargs:
|
||||||
|
payload["saturation"] = round(kwargs[ATTR_HS_COLOR][1])
|
||||||
|
payload["hue"] = round(kwargs[ATTR_HS_COLOR][0])
|
||||||
|
payload = json.dumps(payload)
|
||||||
|
await put_state(
|
||||||
|
self._session,
|
||||||
|
self._api_key,
|
||||||
|
self._attr_unique_id,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs):
|
||||||
|
"""Async function to set off to light."""
|
||||||
|
payload = {"on": False}
|
||||||
|
payload = json.dumps(payload)
|
||||||
|
await put_state(
|
||||||
|
self._session,
|
||||||
|
self._api_key,
|
||||||
|
self._attr_unique_id,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
await self.coordinator.async_request_refresh()
|
11
homeassistant/components/freedompro/manifest.json
Normal file
11
homeassistant/components/freedompro/manifest.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"domain": "freedompro",
|
||||||
|
"name": "Freedompro",
|
||||||
|
"config_flow": true,
|
||||||
|
"documentation": "https://www.home-assistant.io/integrations/freedompro",
|
||||||
|
"codeowners": [
|
||||||
|
"@stefano055415"
|
||||||
|
],
|
||||||
|
"requirements": ["pyfreedompro==1.1.0"],
|
||||||
|
"iot_class": "cloud_polling"
|
||||||
|
}
|
20
homeassistant/components/freedompro/strings.json
Normal file
20
homeassistant/components/freedompro/strings.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||||
|
},
|
||||||
|
"description": "Please enter the API key obtained from https://home.freedompro.eu",
|
||||||
|
"title": "Freedompro API key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
homeassistant/components/freedompro/translations/en.json
Normal file
20
homeassistant/components/freedompro/translations/en.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Device is already configured"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"cannot_connect": "Failed to connect",
|
||||||
|
"invalid_auth": "Invalid authentication"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"api_key": "API Key"
|
||||||
|
},
|
||||||
|
"description": "Please enter the API key obtained from https://home.freedompro.eu",
|
||||||
|
"title": "Freedompro API key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -82,6 +82,7 @@ FLOWS = [
|
|||||||
"forked_daapd",
|
"forked_daapd",
|
||||||
"foscam",
|
"foscam",
|
||||||
"freebox",
|
"freebox",
|
||||||
|
"freedompro",
|
||||||
"fritz",
|
"fritz",
|
||||||
"fritzbox",
|
"fritzbox",
|
||||||
"fritzbox_callmonitor",
|
"fritzbox_callmonitor",
|
||||||
|
@ -1450,6 +1450,9 @@ pyfnip==0.2
|
|||||||
# homeassistant.components.forked_daapd
|
# homeassistant.components.forked_daapd
|
||||||
pyforked-daapd==0.1.11
|
pyforked-daapd==0.1.11
|
||||||
|
|
||||||
|
# homeassistant.components.freedompro
|
||||||
|
pyfreedompro==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.fritzbox
|
# homeassistant.components.fritzbox
|
||||||
pyfritzhome==0.4.2
|
pyfritzhome==0.4.2
|
||||||
|
|
||||||
|
@ -809,6 +809,9 @@ pyflunearyou==1.0.7
|
|||||||
# homeassistant.components.forked_daapd
|
# homeassistant.components.forked_daapd
|
||||||
pyforked-daapd==0.1.11
|
pyforked-daapd==0.1.11
|
||||||
|
|
||||||
|
# homeassistant.components.freedompro
|
||||||
|
pyfreedompro==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.fritzbox
|
# homeassistant.components.fritzbox
|
||||||
pyfritzhome==0.4.2
|
pyfritzhome==0.4.2
|
||||||
|
|
||||||
|
1
tests/components/freedompro/__init__.py
Normal file
1
tests/components/freedompro/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for the Freedompro integration."""
|
67
tests/components/freedompro/conftest.py
Normal file
67
tests/components/freedompro/conftest.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
"""Fixtures for Freedompro integration tests."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.freedompro.const import DOMAIN
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.freedompro.const import DEVICES, DEVICES_STATE
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def init_integration(hass) -> MockConfigEntry:
|
||||||
|
"""Set up the Freedompro integration in Home Assistant."""
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Feedompro",
|
||||||
|
unique_id="0123456",
|
||||||
|
data={
|
||||||
|
"api_key": "gdhsksjdhcncjdkdjndjdkdmndjdjdkd",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.freedompro.get_list",
|
||||||
|
return_value={
|
||||||
|
"state": True,
|
||||||
|
"devices": DEVICES,
|
||||||
|
},
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.freedompro.get_states",
|
||||||
|
return_value=DEVICES_STATE,
|
||||||
|
):
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def init_integration_no_state(hass) -> MockConfigEntry:
|
||||||
|
"""Set up the Freedompro integration in Home Assistant without state."""
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Feedompro",
|
||||||
|
unique_id="0123456",
|
||||||
|
data={
|
||||||
|
"api_key": "gdhsksjdhcncjdkdjndjdkdmndjdjdkd",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.freedompro.get_list",
|
||||||
|
return_value={
|
||||||
|
"state": True,
|
||||||
|
"devices": DEVICES,
|
||||||
|
},
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.freedompro.get_states",
|
||||||
|
return_value=[],
|
||||||
|
):
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
return entry
|
219
tests/components/freedompro/const.py
Normal file
219
tests/components/freedompro/const.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
"""Const Freedompro for test."""
|
||||||
|
|
||||||
|
DEVICES = [
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*2VAS3HTWINNZ5N6HVEIPDJ6NX85P2-AM-GSYWUCNPU0",
|
||||||
|
"name": "Bathroom leak sensor",
|
||||||
|
"type": "leakSensor",
|
||||||
|
"characteristics": ["leakDetected"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "2WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*2VAS3HTWINNZ5N6HVEIPDJ6NX85P2-AM-GSYWUCNPU0",
|
||||||
|
"name": "lock",
|
||||||
|
"type": "lock",
|
||||||
|
"characteristics": ["lock"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*ILYH1E3DWZOVMNEUIMDYMNLOW-LFRQFDPWWJOVHVDOS",
|
||||||
|
"name": "Bedroom fan",
|
||||||
|
"type": "fan",
|
||||||
|
"characteristics": ["on", "rotationSpeed"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*SOT3NKALCRQMHUHJUF79NUG6UQP1IIQIN1PJVRRPT0C",
|
||||||
|
"name": "Contact sensor living room",
|
||||||
|
"type": "contactSensor",
|
||||||
|
"characteristics": ["contactSensorState"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*VTEPEDYE8DXGS8U94CJKQDLKMN6CUX1IJWSOER2HZCK",
|
||||||
|
"name": "Doorway motion sensor",
|
||||||
|
"type": "motionSensor",
|
||||||
|
"characteristics": ["motionDetected"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*QN-DDFMPEPRDOQV7W7JQG3NL0NPZGTLIBYT3HFSPNEY",
|
||||||
|
"name": "Garden humidity sensor",
|
||||||
|
"type": "humiditySensor",
|
||||||
|
"characteristics": ["currentRelativeHumidity"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*1JKU1MVWHQL-Z9SCUS85VFXMRGNDCDNDDUVVDKBU31W",
|
||||||
|
"name": "Irrigation switch",
|
||||||
|
"type": "switch",
|
||||||
|
"characteristics": ["on"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JHJZIZ9ORJNHB7DZNBNAOSEDECVTTZ48SABTCA3WA3M",
|
||||||
|
"name": "lightbulb",
|
||||||
|
"type": "lightbulb",
|
||||||
|
"characteristics": ["on", "brightness", "saturation", "hue"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*SNG7Y3R1R0S_W5BCNPP1O5WUN2NCEOOT27EFSYT6JYS",
|
||||||
|
"name": "Living room occupancy sensor",
|
||||||
|
"type": "occupancySensor",
|
||||||
|
"characteristics": ["occupancyDetected"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*LWPVY7X1AX0DRWLYUUNZ3ZSTHMYNDDBQTPZCZQUUASA",
|
||||||
|
"name": "Living room temperature sensor",
|
||||||
|
"type": "temperatureSensor",
|
||||||
|
"characteristics": ["currentTemperature"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*SXFMEXI4UMDBAMXXPI6LJV47O9NY-IRCAKZI7_MW0LY",
|
||||||
|
"name": "Smoke sensor kitchen",
|
||||||
|
"type": "smokeSensor",
|
||||||
|
"characteristics": ["smokeDetected"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*R6V0FNNF7SACWZ8V9NCOX7UCYI4ODSYAOJWZ80PLJ3C",
|
||||||
|
"name": "Bedroom CO2 sensor",
|
||||||
|
"type": "carbonDioxideSensor",
|
||||||
|
"characteristics": ["carbonDioxideDetected", "carbonDioxideLevel"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*3-QURR5Q6ADA8ML1TBRG59RRGM1F9LVUZLKPYKFJQHC",
|
||||||
|
"name": "bedroomlight",
|
||||||
|
"type": "lightbulb",
|
||||||
|
"characteristics": ["on"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*TWMYQKL3UVED4HSIIB9GXJWJZBQCXG-9VE-N2IUAIWI",
|
||||||
|
"name": "Bedroom thermostat",
|
||||||
|
"type": "thermostat",
|
||||||
|
"characteristics": [
|
||||||
|
"heatingCoolingState",
|
||||||
|
"currentTemperature",
|
||||||
|
"targetTemperature",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*3XSSVIJWK-65HILWTC4WINQK46SP4OEZRCNO25VGWAS",
|
||||||
|
"name": "Bedroom window covering",
|
||||||
|
"type": "windowCovering",
|
||||||
|
"characteristics": ["position"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JVRAR_6WVL1Y0PJ5GFWGPMFV7FLVD4MZKBWXC_UFWYM",
|
||||||
|
"name": "Garden light sensors",
|
||||||
|
"type": "lightSensor",
|
||||||
|
"characteristics": ["currentAmbientLightLevel"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*0PUTVZVJJJL-ZHZZBHTIBS3-J-U7JYNPACFPJW0MD-I",
|
||||||
|
"name": "Living room outlet",
|
||||||
|
"type": "outlet",
|
||||||
|
"characteristics": ["on"],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
DEVICES_STATE = [
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*2VAS3HTWINNZ5N6HVEIPDJ6NX85P2-AM-GSYWUCNPU0",
|
||||||
|
"type": "leakSensor",
|
||||||
|
"state": {"leakDetected": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "2WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*2VAS3HTWINNZ5N6HVEIPDJ6NX85P2-AM-GSYWUCNPU0",
|
||||||
|
"type": "lock",
|
||||||
|
"state": {"lock": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*ILYH1E3DWZOVMNEUIMDYMNLOW-LFRQFDPWWJOVHVDOS",
|
||||||
|
"type": "fan",
|
||||||
|
"state": {"on": False, "rotationSpeed": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*SOT3NKALCRQMHUHJUF79NUG6UQP1IIQIN1PJVRRPT0C",
|
||||||
|
"type": "contactSensor",
|
||||||
|
"state": {"contactSensorState": True},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*VTEPEDYE8DXGS8U94CJKQDLKMN6CUX1IJWSOER2HZCK",
|
||||||
|
"type": "motionSensor",
|
||||||
|
"state": {"motionDetected": False},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*QN-DDFMPEPRDOQV7W7JQG3NL0NPZGTLIBYT3HFSPNEY",
|
||||||
|
"type": "humiditySensor",
|
||||||
|
"state": {"currentRelativeHumidity": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*1JKU1MVWHQL-Z9SCUS85VFXMRGNDCDNDDUVVDKBU31W",
|
||||||
|
"type": "switch",
|
||||||
|
"state": {"on": False},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JHJZIZ9ORJNHB7DZNBNAOSEDECVTTZ48SABTCA3WA3M",
|
||||||
|
"type": "lightbulb",
|
||||||
|
"state": {"on": True, "brightness": 0, "saturation": 0, "hue": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*SNG7Y3R1R0S_W5BCNPP1O5WUN2NCEOOT27EFSYT6JYS",
|
||||||
|
"type": "occupancySensor",
|
||||||
|
"state": {"occupancyDetected": False},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*LWPVY7X1AX0DRWLYUUNZ3ZSTHMYNDDBQTPZCZQUUASA",
|
||||||
|
"type": "temperatureSensor",
|
||||||
|
"state": {"currentTemperature": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*SXFMEXI4UMDBAMXXPI6LJV47O9NY-IRCAKZI7_MW0LY",
|
||||||
|
"type": "smokeSensor",
|
||||||
|
"state": {"smokeDetected": False},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*R6V0FNNF7SACWZ8V9NCOX7UCYI4ODSYAOJWZ80PLJ3C",
|
||||||
|
"type": "carbonDioxideSensor",
|
||||||
|
"state": {"carbonDioxideDetected": False, "carbonDioxideLevel": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*3-QURR5Q6ADA8ML1TBRG59RRGM1F9LVUZLKPYKFJQHC",
|
||||||
|
"type": "lightbulb",
|
||||||
|
"state": {"on": False},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*TWMYQKL3UVED4HSIIB9GXJWJZBQCXG-9VE-N2IUAIWI",
|
||||||
|
"type": "thermostat",
|
||||||
|
"state": {
|
||||||
|
"heatingCoolingState": 1,
|
||||||
|
"currentTemperature": 14,
|
||||||
|
"targetTemperature": 14,
|
||||||
|
},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*3XSSVIJWK-65HILWTC4WINQK46SP4OEZRCNO25VGWAS",
|
||||||
|
"type": "windowCovering",
|
||||||
|
"state": {"position": 0},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JVRAR_6WVL1Y0PJ5GFWGPMFV7FLVD4MZKBWXC_UFWYM",
|
||||||
|
"type": "lightSensor",
|
||||||
|
"state": {"currentAmbientLightLevel": 500},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*0PUTVZVJJJL-ZHZZBHTIBS3-J-U7JYNPACFPJW0MD-I",
|
||||||
|
"type": "outlet",
|
||||||
|
"state": {"on": False},
|
||||||
|
"online": True,
|
||||||
|
},
|
||||||
|
]
|
82
tests/components/freedompro/test_config_flow.py
Normal file
82
tests/components/freedompro/test_config_flow.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""Define tests for the Freedompro config flow."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant import data_entry_flow
|
||||||
|
from homeassistant.components.freedompro.const import DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
|
from homeassistant.const import CONF_API_KEY
|
||||||
|
|
||||||
|
from tests.components.freedompro.const import DEVICES
|
||||||
|
|
||||||
|
VALID_CONFIG = {
|
||||||
|
CONF_API_KEY: "ksdjfgslkjdfksjdfksjgfksjd",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_show_form(hass):
|
||||||
|
"""Test that the form is served with no input."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == SOURCE_USER
|
||||||
|
|
||||||
|
|
||||||
|
async def test_invalid_auth(hass):
|
||||||
|
"""Test that errors are shown when API key is invalid."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.freedompro.config_flow.list",
|
||||||
|
return_value={
|
||||||
|
"state": False,
|
||||||
|
"code": -201,
|
||||||
|
},
|
||||||
|
):
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data=VALID_CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_connection_error(hass):
|
||||||
|
"""Test that errors are shown when API key is invalid."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.freedompro.config_flow.get_list",
|
||||||
|
return_value={
|
||||||
|
"state": False,
|
||||||
|
"code": -200,
|
||||||
|
},
|
||||||
|
):
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data=VALID_CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_create_entry(hass):
|
||||||
|
"""Test that the user step works."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.freedompro.config_flow.get_list",
|
||||||
|
return_value={
|
||||||
|
"state": True,
|
||||||
|
"devices": DEVICES,
|
||||||
|
},
|
||||||
|
):
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data=VALID_CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == "Freedompro"
|
||||||
|
assert result["data"][CONF_API_KEY] == "ksdjfgslkjdfksjdfksjgfksjd"
|
55
tests/components/freedompro/test_init.py
Normal file
55
tests/components/freedompro/test_init.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""Freedompro component tests."""
|
||||||
|
import logging
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant.components.freedompro.const import DOMAIN
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ENTITY_ID = f"{DOMAIN}.fake_name"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_setup_entry(hass, init_integration):
|
||||||
|
"""Test a successful setup entry."""
|
||||||
|
entry = init_integration
|
||||||
|
assert entry is not None
|
||||||
|
state = hass.states
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_config_not_ready(hass):
|
||||||
|
"""Test for setup failure if connection to Freedompro is missing."""
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Feedompro",
|
||||||
|
unique_id="0123456",
|
||||||
|
data={
|
||||||
|
"api_key": "gdhsksjdhcncjdkdjndjdkdmndjdjdkd",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.freedompro.get_list",
|
||||||
|
return_value={
|
||||||
|
"state": False,
|
||||||
|
},
|
||||||
|
):
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
assert entry.state == ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unload_entry(hass, init_integration):
|
||||||
|
"""Test successful unload of entry."""
|
||||||
|
entry = init_integration
|
||||||
|
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
assert entry.state == ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert entry.state == ConfigEntryState.NOT_LOADED
|
155
tests/components/freedompro/test_light.py
Normal file
155
tests/components/freedompro/test_light.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
"""Tests for the Freedompro light."""
|
||||||
|
from homeassistant.components.light import (
|
||||||
|
ATTR_BRIGHTNESS,
|
||||||
|
ATTR_HS_COLOR,
|
||||||
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
)
|
||||||
|
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, STATE_OFF, STATE_ON
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_get_state(hass, init_integration):
|
||||||
|
"""Test states of the light."""
|
||||||
|
init_integration
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
entity_id = "light.lightbulb"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes.get("friendly_name") == "lightbulb"
|
||||||
|
|
||||||
|
entry = registry.async_get(entity_id)
|
||||||
|
assert entry
|
||||||
|
assert (
|
||||||
|
entry.unique_id
|
||||||
|
== "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JHJZIZ9ORJNHB7DZNBNAOSEDECVTTZ48SABTCA3WA3M"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_set_on(hass, init_integration):
|
||||||
|
"""Test set on of the light."""
|
||||||
|
init_integration
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
entity_id = "light.lightbulb"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes.get("friendly_name") == "lightbulb"
|
||||||
|
|
||||||
|
entry = registry.async_get(entity_id)
|
||||||
|
assert entry
|
||||||
|
assert (
|
||||||
|
entry.unique_id
|
||||||
|
== "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JHJZIZ9ORJNHB7DZNBNAOSEDECVTTZ48SABTCA3WA3M"
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: [entity_id]},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_set_off(hass, init_integration):
|
||||||
|
"""Test set off of the light."""
|
||||||
|
init_integration
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
entity_id = "light.bedroomlight"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes.get("friendly_name") == "bedroomlight"
|
||||||
|
|
||||||
|
entry = registry.async_get(entity_id)
|
||||||
|
assert entry
|
||||||
|
assert (
|
||||||
|
entry.unique_id
|
||||||
|
== "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*3-QURR5Q6ADA8ML1TBRG59RRGM1F9LVUZLKPYKFJQHC"
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: [entity_id]},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_set_brightness(hass, init_integration):
|
||||||
|
"""Test set brightness of the light."""
|
||||||
|
init_integration
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
entity_id = "light.lightbulb"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes.get("friendly_name") == "lightbulb"
|
||||||
|
|
||||||
|
entry = registry.async_get(entity_id)
|
||||||
|
assert entry
|
||||||
|
assert (
|
||||||
|
entry.unique_id
|
||||||
|
== "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JHJZIZ9ORJNHB7DZNBNAOSEDECVTTZ48SABTCA3WA3M"
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: [entity_id], ATTR_BRIGHTNESS: 255},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert int(state.attributes[ATTR_BRIGHTNESS]) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_light_set_hue(hass, init_integration):
|
||||||
|
"""Test set brightness of the light."""
|
||||||
|
init_integration
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
entity_id = "light.lightbulb"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes.get("friendly_name") == "lightbulb"
|
||||||
|
|
||||||
|
entry = registry.async_get(entity_id)
|
||||||
|
assert entry
|
||||||
|
assert (
|
||||||
|
entry.unique_id
|
||||||
|
== "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JHJZIZ9ORJNHB7DZNBNAOSEDECVTTZ48SABTCA3WA3M"
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: [entity_id],
|
||||||
|
ATTR_BRIGHTNESS: 255,
|
||||||
|
ATTR_HS_COLOR: (352.32, 100.0),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert int(state.attributes[ATTR_BRIGHTNESS]) == 0
|
||||||
|
assert state.attributes[ATTR_HS_COLOR] == (0, 0)
|
Loading…
x
Reference in New Issue
Block a user