Add state belief services to bond integration (#54735)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
joshs85 2021-09-09 09:32:32 -04:00 committed by GitHub
parent cbbbc3c4f0
commit 011817b122
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 678 additions and 10 deletions

View File

@ -73,7 +73,7 @@ homeassistant/components/blink/* @fronzbot
homeassistant/components/blueprint/* @home-assistant/core homeassistant/components/blueprint/* @home-assistant/core
homeassistant/components/bmp280/* @belidzs homeassistant/components/bmp280/* @belidzs
homeassistant/components/bmw_connected_drive/* @gerard33 @rikroe homeassistant/components/bmw_connected_drive/* @gerard33 @rikroe
homeassistant/components/bond/* @prystupa homeassistant/components/bond/* @prystupa @joshs85
homeassistant/components/bosch_shc/* @tschamm homeassistant/components/bosch_shc/* @tschamm
homeassistant/components/braviatv/* @bieniu @Drafteed homeassistant/components/braviatv/* @bieniu @Drafteed
homeassistant/components/broadlink/* @danielhiversen @felipediel homeassistant/components/broadlink/* @danielhiversen @felipediel

View File

@ -10,3 +10,9 @@ CONF_BOND_ID: str = "bond_id"
HUB = "hub" HUB = "hub"
BPUP_SUBS = "bpup_subs" BPUP_SUBS = "bpup_subs"
BPUP_STOP = "bpup_stop" BPUP_STOP = "bpup_stop"
SERVICE_SET_FAN_SPEED_BELIEF = "set_fan_speed_belief"
SERVICE_SET_POWER_BELIEF = "set_switch_power_belief"
SERVICE_SET_LIGHT_POWER_BELIEF = "set_light_power_belief"
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF = "set_light_brightness_belief"
ATTR_POWER_STATE = "power_state"

View File

@ -5,9 +5,12 @@ import logging
import math import math
from typing import Any from typing import Any
from aiohttp.client_exceptions import ClientResponseError
from bond_api import Action, BPUPSubscriptions, DeviceType, Direction from bond_api import Action, BPUPSubscriptions, DeviceType, Direction
import voluptuous as vol
from homeassistant.components.fan import ( from homeassistant.components.fan import (
ATTR_SPEED,
DIRECTION_FORWARD, DIRECTION_FORWARD,
DIRECTION_REVERSE, DIRECTION_REVERSE,
SUPPORT_DIRECTION, SUPPORT_DIRECTION,
@ -16,6 +19,8 @@ from homeassistant.components.fan import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.percentage import ( from homeassistant.util.percentage import (
@ -24,7 +29,7 @@ from homeassistant.util.percentage import (
ranged_value_to_percentage, ranged_value_to_percentage,
) )
from .const import BPUP_SUBS, DOMAIN, HUB from .const import BPUP_SUBS, DOMAIN, HUB, SERVICE_SET_FAN_SPEED_BELIEF
from .entity import BondEntity from .entity import BondEntity
from .utils import BondDevice, BondHub from .utils import BondDevice, BondHub
@ -40,6 +45,7 @@ async def async_setup_entry(
data = hass.data[DOMAIN][entry.entry_id] data = hass.data[DOMAIN][entry.entry_id]
hub: BondHub = data[HUB] hub: BondHub = data[HUB]
bpup_subs: BPUPSubscriptions = data[BPUP_SUBS] bpup_subs: BPUPSubscriptions = data[BPUP_SUBS]
platform = entity_platform.async_get_current_platform()
fans: list[Entity] = [ fans: list[Entity] = [
BondFan(hub, device, bpup_subs) BondFan(hub, device, bpup_subs)
@ -47,6 +53,12 @@ async def async_setup_entry(
if DeviceType.is_fan(device.type) if DeviceType.is_fan(device.type)
] ]
platform.async_register_entity_service(
SERVICE_SET_FAN_SPEED_BELIEF,
{vol.Required(ATTR_SPEED): vol.All(vol.Number(scale=0), vol.Range(0, 100))},
"async_set_speed_belief",
)
async_add_entities(fans, True) async_add_entities(fans, True)
@ -128,6 +140,41 @@ class BondFan(BondEntity, FanEntity):
self._device.device_id, Action.set_speed(bond_speed) self._device.device_id, Action.set_speed(bond_speed)
) )
async def async_set_power_belief(self, power_state: bool) -> None:
"""Set the believed state to on or off."""
try:
await self._hub.bond.action(
self._device.device_id, Action.set_power_state_belief(power_state)
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_power_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex
async def async_set_speed_belief(self, speed: int) -> None:
"""Set the believed speed for the fan."""
_LOGGER.debug("async_set_speed_belief called with percentage %s", speed)
if speed == 0:
await self.async_set_power_belief(False)
return
await self.async_set_power_belief(True)
bond_speed = math.ceil(percentage_to_ranged_value(self._speed_range, speed))
_LOGGER.debug(
"async_set_percentage converted percentage %s to bond speed %s",
speed,
bond_speed,
)
try:
await self._hub.bond.action(
self._device.device_id, Action.set_speed_belief(bond_speed)
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_speed_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex
async def async_turn_on( async def async_turn_on(
self, self,
speed: str | None = None, speed: str | None = None,

View File

@ -4,7 +4,9 @@ from __future__ import annotations
import logging import logging
from typing import Any from typing import Any
from aiohttp.client_exceptions import ClientResponseError
from bond_api import Action, BPUPSubscriptions, DeviceType from bond_api import Action, BPUPSubscriptions, DeviceType
import voluptuous as vol
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
@ -14,12 +16,19 @@ from homeassistant.components.light import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BondHub from . import BondHub
from .const import BPUP_SUBS, DOMAIN, HUB from .const import (
ATTR_POWER_STATE,
BPUP_SUBS,
DOMAIN,
HUB,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
SERVICE_SET_LIGHT_POWER_BELIEF,
)
from .entity import BondEntity from .entity import BondEntity
from .utils import BondDevice from .utils import BondDevice
@ -45,6 +54,7 @@ async def async_setup_entry(
data = hass.data[DOMAIN][entry.entry_id] data = hass.data[DOMAIN][entry.entry_id]
hub: BondHub = data[HUB] hub: BondHub = data[HUB]
bpup_subs: BPUPSubscriptions = data[BPUP_SUBS] bpup_subs: BPUPSubscriptions = data[BPUP_SUBS]
platform = entity_platform.async_get_current_platform()
platform = entity_platform.async_get_current_platform() platform = entity_platform.async_get_current_platform()
for service in ENTITY_SERVICES: for service in ENTITY_SERVICES:
@ -92,6 +102,22 @@ async def async_setup_entry(
if DeviceType.is_light(device.type) if DeviceType.is_light(device.type)
] ]
platform.async_register_entity_service(
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{
vol.Required(ATTR_BRIGHTNESS): vol.All(
vol.Number(scale=0), vol.Range(0, 255)
)
},
"async_set_brightness_belief",
)
platform.async_register_entity_service(
SERVICE_SET_LIGHT_POWER_BELIEF,
{vol.Required(ATTR_POWER_STATE): vol.All(cv.boolean)},
"async_set_power_belief",
)
async_add_entities( async_add_entities(
fan_lights + fan_up_lights + fan_down_lights + fireplaces + fp_lights + lights, fan_lights + fan_up_lights + fan_down_lights + fireplaces + fp_lights + lights,
True, True,
@ -103,6 +129,34 @@ class BondBaseLight(BondEntity, LightEntity):
_attr_supported_features = 0 _attr_supported_features = 0
async def async_set_brightness_belief(self, brightness: int) -> None:
"""Set the belief state of the light."""
if not self._device.supports_set_brightness():
raise HomeAssistantError("This device does not support setting brightness")
if brightness == 0:
await self.async_set_power_belief(False)
return
try:
await self._hub.bond.action(
self._device.device_id,
Action.set_brightness_belief(round((brightness * 100) / 255)),
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_brightness_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex
async def async_set_power_belief(self, power_state: bool) -> None:
"""Set the belief state of the light."""
try:
await self._hub.bond.action(
self._device.device_id, Action.set_light_state_belief(power_state)
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_light_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex
class BondLight(BondBaseLight, BondEntity, LightEntity): class BondLight(BondBaseLight, BondEntity, LightEntity):
"""Representation of a Bond light.""" """Representation of a Bond light."""
@ -231,3 +285,31 @@ class BondFireplace(BondEntity, LightEntity):
_LOGGER.debug("Fireplace async_turn_off called with: %s", kwargs) _LOGGER.debug("Fireplace async_turn_off called with: %s", kwargs)
await self._hub.bond.action(self._device.device_id, Action.turn_off()) await self._hub.bond.action(self._device.device_id, Action.turn_off())
async def async_set_brightness_belief(self, brightness: int) -> None:
"""Set the belief state of the light."""
if not self._device.supports_set_brightness():
raise HomeAssistantError("This device does not support setting brightness")
if brightness == 0:
await self.async_set_power_belief(False)
return
try:
await self._hub.bond.action(
self._device.device_id,
Action.set_brightness_belief(round((brightness * 100) / 255)),
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_brightness_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex
async def async_set_power_belief(self, power_state: bool) -> None:
"""Set the belief state of the light."""
try:
await self._hub.bond.action(
self._device.device_id, Action.set_power_state_belief(power_state)
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_power_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex

View File

@ -3,9 +3,9 @@
"name": "Bond", "name": "Bond",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bond", "documentation": "https://www.home-assistant.io/integrations/bond",
"requirements": ["bond-api==0.1.12"], "requirements": ["bond-api==0.1.13"],
"zeroconf": ["_bond._tcp.local."], "zeroconf": ["_bond._tcp.local."],
"codeowners": ["@prystupa"], "codeowners": ["@prystupa", "@joshs85"],
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "local_push" "iot_class": "local_push"
} }

View File

@ -1,3 +1,97 @@
# Describes the format for available bond services
set_fan_speed_belief:
name: Set believed fan speed
description: Sets the believed fan speed for a bond fan
fields:
entity_id:
description: Name(s) of entities to set the believed fan speed.
example: "fan.living_room_fan"
name: Entity
required: true
selector:
entity:
integration: bond
domain: fan
speed:
required: true
name: Fan Speed
description: Fan Speed as %.
example: 50
selector:
number:
min: 0
max: 100
step: 1
mode: slider
set_switch_power_belief:
name: Set believed switch power state
description: Sets the believed power state of a bond switch
fields:
entity_id:
description: Name(s) of entities to set the believed power state of.
example: "switch.whatever"
name: Entity
required: true
selector:
entity:
integration: bond
domain: switch
power_state:
required: true
name: Power state
description: Power state
example: true
selector:
boolean:
set_light_power_belief:
name: Set believed light power state
description: Sets the believed light power state of a bond light
fields:
entity_id:
description: Name(s) of entities to set the believed power state of.
example: "light.living_room_lights"
name: Entity
required: true
selector:
entity:
integration: bond
domain: light
power_state:
required: true
name: Power state
description: Power state
example: true
selector:
boolean:
set_light_brightness_belief:
name: Set believed light brightness state
description: Sets the believed light brightness state of a bond light
fields:
entity_id:
description: Name(s) of entities to set the believed power state of.
example: "light.living_room_lights"
name: Entity
required: true
selector:
entity:
integration: bond
domain: light
brightness:
required: true
name: Brightness
description: Brightness
example: 50
selector:
number:
min: 0
max: 255
step: 1
mode: slider
start_increasing_brightness: start_increasing_brightness:
name: Start increasing brightness name: Start increasing brightness
description: "Start increasing the brightness of the light." description: "Start increasing the brightness of the light."
@ -21,3 +115,4 @@ stop:
entity: entity:
integration: bond integration: bond
domain: light domain: light

View File

@ -3,15 +3,19 @@ from __future__ import annotations
from typing import Any from typing import Any
from aiohttp.client_exceptions import ClientResponseError
from bond_api import Action, BPUPSubscriptions, DeviceType from bond_api import Action, BPUPSubscriptions, DeviceType
import voluptuous as vol
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import BPUP_SUBS, DOMAIN, HUB from .const import ATTR_POWER_STATE, BPUP_SUBS, DOMAIN, HUB, SERVICE_SET_POWER_BELIEF
from .entity import BondEntity from .entity import BondEntity
from .utils import BondHub from .utils import BondHub
@ -25,6 +29,7 @@ async def async_setup_entry(
data = hass.data[DOMAIN][entry.entry_id] data = hass.data[DOMAIN][entry.entry_id]
hub: BondHub = data[HUB] hub: BondHub = data[HUB]
bpup_subs: BPUPSubscriptions = data[BPUP_SUBS] bpup_subs: BPUPSubscriptions = data[BPUP_SUBS]
platform = entity_platform.async_get_current_platform()
switches: list[Entity] = [ switches: list[Entity] = [
BondSwitch(hub, device, bpup_subs) BondSwitch(hub, device, bpup_subs)
@ -32,6 +37,12 @@ async def async_setup_entry(
if DeviceType.is_generic(device.type) if DeviceType.is_generic(device.type)
] ]
platform.async_register_entity_service(
SERVICE_SET_POWER_BELIEF,
{vol.Required(ATTR_POWER_STATE): cv.boolean},
"async_set_power_belief",
)
async_add_entities(switches, True) async_add_entities(switches, True)
@ -48,3 +59,14 @@ class BondSwitch(BondEntity, SwitchEntity):
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off.""" """Turn the device off."""
await self._hub.bond.action(self._device.device_id, Action.turn_off()) await self._hub.bond.action(self._device.device_id, Action.turn_off())
async def async_set_power_belief(self, power_state: bool) -> None:
"""Set switch power belief."""
try:
await self._hub.bond.action(
self._device.device_id, Action.set_power_state_belief(power_state)
)
except ClientResponseError as ex:
raise HomeAssistantError(
f"The bond API returned an error calling set_power_state_belief for {self.entity_id}. Code: {ex.code} Message: {ex.message}"
) from ex

View File

@ -406,7 +406,7 @@ blockchain==1.4.4
# bme680==1.0.5 # bme680==1.0.5
# homeassistant.components.bond # homeassistant.components.bond
bond-api==0.1.12 bond-api==0.1.13
# homeassistant.components.bosch_shc # homeassistant.components.bosch_shc
boschshcpy==0.2.19 boschshcpy==0.2.19

View File

@ -239,7 +239,7 @@ blebox_uniapi==1.3.3
blinkpy==0.17.0 blinkpy==0.17.0
# homeassistant.components.bond # homeassistant.components.bond
bond-api==0.1.12 bond-api==0.1.13
# homeassistant.components.bosch_shc # homeassistant.components.bosch_shc
boschshcpy==0.2.19 boschshcpy==0.2.19

View File

@ -7,6 +7,8 @@ from datetime import timedelta
from typing import Any from typing import Any
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from aiohttp.client_exceptions import ClientResponseError
from homeassistant import core from homeassistant import core
from homeassistant.components.bond.const import DOMAIN as BOND_DOMAIN from homeassistant.components.bond.const import DOMAIN as BOND_DOMAIN
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, STATE_UNAVAILABLE from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, STATE_UNAVAILABLE
@ -184,6 +186,16 @@ def patch_bond_action():
return patch("homeassistant.components.bond.Bond.action") return patch("homeassistant.components.bond.Bond.action")
def patch_bond_action_returns_clientresponseerror():
"""Patch Bond API action endpoint to throw ClientResponseError."""
return patch(
"homeassistant.components.bond.Bond.action",
side_effect=ClientResponseError(
request_info=None, history=None, code=405, message="Method Not Allowed"
),
)
def patch_bond_device_properties(return_value=None): def patch_bond_device_properties(return_value=None):
"""Patch Bond API device properties endpoint.""" """Patch Bond API device properties endpoint."""
if return_value is None: if return_value is None:

View File

@ -4,9 +4,14 @@ from __future__ import annotations
from datetime import timedelta from datetime import timedelta
from bond_api import Action, DeviceType, Direction from bond_api import Action, DeviceType, Direction
import pytest
from homeassistant import core from homeassistant import core
from homeassistant.components import fan from homeassistant.components import fan
from homeassistant.components.bond.const import (
DOMAIN as BOND_DOMAIN,
SERVICE_SET_FAN_SPEED_BELIEF,
)
from homeassistant.components.fan import ( from homeassistant.components.fan import (
ATTR_DIRECTION, ATTR_DIRECTION,
ATTR_SPEED, ATTR_SPEED,
@ -19,6 +24,7 @@ from homeassistant.components.fan import (
SPEED_OFF, SPEED_OFF,
) )
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_registry import EntityRegistry from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.util import utcnow from homeassistant.util import utcnow
@ -26,6 +32,7 @@ from homeassistant.util import utcnow
from .common import ( from .common import (
help_test_entity_available, help_test_entity_available,
patch_bond_action, patch_bond_action,
patch_bond_action_returns_clientresponseerror,
patch_bond_device_state, patch_bond_device_state,
setup_platform, setup_platform,
) )
@ -254,6 +261,63 @@ async def test_turn_off_fan(hass: core.HomeAssistant):
mock_turn_off.assert_called_once_with("test-device-id", Action.turn_off()) mock_turn_off.assert_called_once_with("test-device-id", Action.turn_off())
async def test_set_speed_belief_speed_zero(hass: core.HomeAssistant):
"""Tests that set power belief service delegates to API."""
await setup_platform(
hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id"
)
with patch_bond_action() as mock_action, patch_bond_device_state():
await hass.services.async_call(
BOND_DOMAIN,
SERVICE_SET_FAN_SPEED_BELIEF,
{ATTR_ENTITY_ID: "fan.name_1", ATTR_SPEED: 0},
blocking=True,
)
await hass.async_block_till_done()
mock_action.assert_called_once_with(
"test-device-id", Action.set_power_state_belief(False)
)
async def test_set_speed_belief_speed_api_error(hass: core.HomeAssistant):
"""Tests that set power belief service delegates to API."""
await setup_platform(
hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id"
)
with pytest.raises(
HomeAssistantError
), patch_bond_action_returns_clientresponseerror(), patch_bond_device_state():
await hass.services.async_call(
BOND_DOMAIN,
SERVICE_SET_FAN_SPEED_BELIEF,
{ATTR_ENTITY_ID: "fan.name_1", ATTR_SPEED: 100},
blocking=True,
)
await hass.async_block_till_done()
async def test_set_speed_belief_speed_100(hass: core.HomeAssistant):
"""Tests that set power belief service delegates to API."""
await setup_platform(
hass, FAN_DOMAIN, ceiling_fan("name-1"), bond_device_id="test-device-id"
)
with patch_bond_action() as mock_action, patch_bond_device_state():
await hass.services.async_call(
BOND_DOMAIN,
SERVICE_SET_FAN_SPEED_BELIEF,
{ATTR_ENTITY_ID: "fan.name_1", ATTR_SPEED: 100},
blocking=True,
)
await hass.async_block_till_done()
mock_action.assert_any_call("test-device-id", Action.set_power_state_belief(True))
mock_action.assert_called_with("test-device-id", Action.set_speed_belief(3))
async def test_update_reports_fan_on(hass: core.HomeAssistant): async def test_update_reports_fan_on(hass: core.HomeAssistant):
"""Tests that update command sets correct state when Bond API reports fan power is on.""" """Tests that update command sets correct state when Bond API reports fan power is on."""
await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1")) await setup_platform(hass, FAN_DOMAIN, ceiling_fan("name-1"))

View File

@ -5,7 +5,12 @@ from bond_api import Action, DeviceType
import pytest import pytest
from homeassistant import core from homeassistant import core
from homeassistant.components.bond.const import DOMAIN from homeassistant.components.bond.const import (
ATTR_POWER_STATE,
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
SERVICE_SET_LIGHT_POWER_BELIEF,
)
from homeassistant.components.bond.light import ( from homeassistant.components.bond.light import (
SERVICE_START_DECREASING_BRIGHTNESS, SERVICE_START_DECREASING_BRIGHTNESS,
SERVICE_START_INCREASING_BRIGHTNESS, SERVICE_START_INCREASING_BRIGHTNESS,
@ -31,6 +36,7 @@ from homeassistant.util import utcnow
from .common import ( from .common import (
help_test_entity_available, help_test_entity_available,
patch_bond_action, patch_bond_action,
patch_bond_action_returns_clientresponseerror,
patch_bond_device_state, patch_bond_device_state,
setup_platform, setup_platform,
) )
@ -47,6 +53,15 @@ def light(name: str):
} }
def light_no_brightness(name: str):
"""Create a light with a given name."""
return {
"name": name,
"type": DeviceType.LIGHT,
"actions": [Action.TURN_LIGHT_ON, Action.TURN_LIGHT_OFF],
}
def ceiling_fan(name: str): def ceiling_fan(name: str):
"""Create a ceiling fan (that has built-in light) with given name.""" """Create a ceiling fan (that has built-in light) with given name."""
return { return {
@ -106,6 +121,21 @@ def fireplace_with_light(name: str):
} }
def fireplace_with_light_supports_brightness(name: str):
"""Create a fireplace with given name."""
return {
"name": name,
"type": DeviceType.FIREPLACE,
"actions": [
Action.TURN_ON,
Action.TURN_OFF,
Action.TURN_LIGHT_ON,
Action.TURN_LIGHT_OFF,
Action.SET_BRIGHTNESS,
],
}
def light_brightness_increase_decrease_only(name: str): def light_brightness_increase_decrease_only(name: str):
"""Create a light that can only increase or decrease brightness.""" """Create a light that can only increase or decrease brightness."""
return { return {
@ -254,6 +284,270 @@ async def test_no_trust_state(hass: core.HomeAssistant):
assert device.attributes.get(ATTR_ASSUMED_STATE) is not True assert device.attributes.get(ATTR_ASSUMED_STATE) is not True
async def test_light_set_brightness_belief_full(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light delegates to API."""
await setup_platform(
hass,
LIGHT_DOMAIN,
light("name-1"),
bond_device_id="test-device-id",
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 255},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_brightness_belief(brightness=100)
)
async def test_light_set_brightness_belief_api_error(hass: core.HomeAssistant):
"""Tests that the set brightness belief throws HomeAssistantError in the event of an api error."""
await setup_platform(
hass,
LIGHT_DOMAIN,
light("name-1"),
bond_device_id="test-device-id",
)
with pytest.raises(
HomeAssistantError
), patch_bond_action_returns_clientresponseerror(), patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 255},
blocking=True,
)
await hass.async_block_till_done()
async def test_fp_light_set_brightness_belief_full(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light delegates to API."""
await setup_platform(
hass,
LIGHT_DOMAIN,
fireplace_with_light_supports_brightness("name-1"),
bond_device_id="test-device-id",
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 255},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_brightness_belief(brightness=100)
)
async def test_fp_light_set_brightness_belief_api_error(hass: core.HomeAssistant):
"""Tests that the set brightness belief throws HomeAssistantError in the event of an api error."""
await setup_platform(
hass,
LIGHT_DOMAIN,
fireplace_with_light_supports_brightness("name-1"),
bond_device_id="test-device-id",
)
with pytest.raises(
HomeAssistantError
), patch_bond_action_returns_clientresponseerror(), patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 255},
blocking=True,
)
await hass.async_block_till_done()
async def test_light_set_brightness_belief_brightnes_not_supported(
hass: core.HomeAssistant,
):
"""Tests that the set brightness belief function of a light that doesn't support setting brightness returns an error."""
await setup_platform(
hass,
LIGHT_DOMAIN,
light_no_brightness("name-1"),
bond_device_id="test-device-id",
)
with pytest.raises(HomeAssistantError), patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 255},
blocking=True,
)
await hass.async_block_till_done()
async def test_light_set_brightness_belief_zero(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light delegates to API."""
await setup_platform(
hass,
LIGHT_DOMAIN,
light("name-1"),
bond_device_id="test-device-id",
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 0},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_light_state_belief(False)
)
async def test_fp_light_set_brightness_belief_zero(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light delegates to API."""
await setup_platform(
hass,
LIGHT_DOMAIN,
fireplace_with_light_supports_brightness("name-1"),
bond_device_id="test-device-id",
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 0},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_power_state_belief(False)
)
async def test_light_set_power_belief(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light delegates to API."""
await setup_platform(
hass,
LIGHT_DOMAIN,
light("name-1"),
bond_device_id="test-device-id",
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_POWER_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_POWER_STATE: False},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_light_state_belief(False)
)
async def test_light_set_power_belief_api_error(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light throws HomeAssistantError in the event of an api error."""
await setup_platform(
hass,
LIGHT_DOMAIN,
light("name-1"),
bond_device_id="test-device-id",
)
with pytest.raises(
HomeAssistantError
), patch_bond_action_returns_clientresponseerror(), patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_POWER_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_POWER_STATE: False},
blocking=True,
)
await hass.async_block_till_done()
async def test_fp_light_set_power_belief(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light delegates to API."""
await setup_platform(
hass,
LIGHT_DOMAIN,
fireplace_with_light("name-1"),
bond_device_id="test-device-id",
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_POWER_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_POWER_STATE: False},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_power_state_belief(False)
)
async def test_fp_light_set_power_belief_api_error(hass: core.HomeAssistant):
"""Tests that the set brightness belief function of a light throws HomeAssistantError in the event of an api error."""
await setup_platform(
hass,
LIGHT_DOMAIN,
fireplace_with_light("name-1"),
bond_device_id="test-device-id",
)
with pytest.raises(
HomeAssistantError
), patch_bond_action_returns_clientresponseerror(), patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_POWER_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_POWER_STATE: False},
blocking=True,
)
await hass.async_block_till_done()
async def test_fp_light_set_brightness_belief_brightnes_not_supported(
hass: core.HomeAssistant,
):
"""Tests that the set brightness belief function of a fireplace light that doesn't support setting brightness returns an error."""
await setup_platform(
hass,
LIGHT_DOMAIN,
fireplace_with_light("name-1"),
bond_device_id="test-device-id",
)
with pytest.raises(HomeAssistantError), patch_bond_device_state():
await hass.services.async_call(
DOMAIN,
SERVICE_SET_LIGHT_BRIGHTNESS_BELIEF,
{ATTR_ENTITY_ID: "light.name_1", ATTR_BRIGHTNESS: 255},
blocking=True,
)
await hass.async_block_till_done()
async def test_light_start_increasing_brightness(hass: core.HomeAssistant): async def test_light_start_increasing_brightness(hass: core.HomeAssistant):
"""Tests a light that can only increase or decrease brightness delegates to API can start increasing brightness.""" """Tests a light that can only increase or decrease brightness delegates to API can start increasing brightness."""
await setup_platform( await setup_platform(

View File

@ -2,10 +2,17 @@
from datetime import timedelta from datetime import timedelta
from bond_api import Action, DeviceType from bond_api import Action, DeviceType
import pytest
from homeassistant import core from homeassistant import core
from homeassistant.components.bond.const import (
ATTR_POWER_STATE,
DOMAIN as BOND_DOMAIN,
SERVICE_SET_POWER_BELIEF,
)
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_registry import EntityRegistry from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.util import utcnow from homeassistant.util import utcnow
@ -13,6 +20,7 @@ from homeassistant.util import utcnow
from .common import ( from .common import (
help_test_entity_available, help_test_entity_available,
patch_bond_action, patch_bond_action,
patch_bond_action_returns_clientresponseerror,
patch_bond_device_state, patch_bond_device_state,
setup_platform, setup_platform,
) )
@ -76,6 +84,44 @@ async def test_turn_off_switch(hass: core.HomeAssistant):
mock_turn_off.assert_called_once_with("test-device-id", Action.turn_off()) mock_turn_off.assert_called_once_with("test-device-id", Action.turn_off())
async def test_switch_set_power_belief(hass: core.HomeAssistant):
"""Tests that the set power belief service delegates to API."""
await setup_platform(
hass, SWITCH_DOMAIN, generic_device("name-1"), bond_device_id="test-device-id"
)
with patch_bond_action() as mock_bond_action, patch_bond_device_state():
await hass.services.async_call(
BOND_DOMAIN,
SERVICE_SET_POWER_BELIEF,
{ATTR_ENTITY_ID: "switch.name_1", ATTR_POWER_STATE: False},
blocking=True,
)
await hass.async_block_till_done()
mock_bond_action.assert_called_once_with(
"test-device-id", Action.set_power_state_belief(False)
)
async def test_switch_set_power_belief_api_error(hass: core.HomeAssistant):
"""Tests that the set power belief service throws HomeAssistantError in the event of an api error."""
await setup_platform(
hass, SWITCH_DOMAIN, generic_device("name-1"), bond_device_id="test-device-id"
)
with pytest.raises(
HomeAssistantError
), patch_bond_action_returns_clientresponseerror(), patch_bond_device_state():
await hass.services.async_call(
BOND_DOMAIN,
SERVICE_SET_POWER_BELIEF,
{ATTR_ENTITY_ID: "switch.name_1", ATTR_POWER_STATE: False},
blocking=True,
)
await hass.async_block_till_done()
async def test_update_reports_switch_is_on(hass: core.HomeAssistant): async def test_update_reports_switch_is_on(hass: core.HomeAssistant):
"""Tests that update command sets correct state when Bond API reports the device is on.""" """Tests that update command sets correct state when Bond API reports the device is on."""
await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1")) await setup_platform(hass, SWITCH_DOMAIN, generic_device("name-1"))