diff --git a/CODEOWNERS b/CODEOWNERS index 35572933213..db4a5b04069 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -73,7 +73,7 @@ homeassistant/components/blink/* @fronzbot homeassistant/components/blueprint/* @home-assistant/core homeassistant/components/bmp280/* @belidzs homeassistant/components/bmw_connected_drive/* @gerard33 @rikroe -homeassistant/components/bond/* @prystupa +homeassistant/components/bond/* @prystupa @joshs85 homeassistant/components/bosch_shc/* @tschamm homeassistant/components/braviatv/* @bieniu @Drafteed homeassistant/components/broadlink/* @danielhiversen @felipediel diff --git a/homeassistant/components/bond/const.py b/homeassistant/components/bond/const.py index 818288a5764..bf1af003e96 100644 --- a/homeassistant/components/bond/const.py +++ b/homeassistant/components/bond/const.py @@ -10,3 +10,9 @@ CONF_BOND_ID: str = "bond_id" HUB = "hub" BPUP_SUBS = "bpup_subs" 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" diff --git a/homeassistant/components/bond/fan.py b/homeassistant/components/bond/fan.py index 92ce0b81658..a5e10cd371a 100644 --- a/homeassistant/components/bond/fan.py +++ b/homeassistant/components/bond/fan.py @@ -5,9 +5,12 @@ import logging import math from typing import Any +from aiohttp.client_exceptions import ClientResponseError from bond_api import Action, BPUPSubscriptions, DeviceType, Direction +import voluptuous as vol from homeassistant.components.fan import ( + ATTR_SPEED, DIRECTION_FORWARD, DIRECTION_REVERSE, SUPPORT_DIRECTION, @@ -16,6 +19,8 @@ from homeassistant.components.fan import ( ) from homeassistant.config_entries import ConfigEntry 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_platform import AddEntitiesCallback from homeassistant.util.percentage import ( @@ -24,7 +29,7 @@ from homeassistant.util.percentage import ( 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 .utils import BondDevice, BondHub @@ -40,6 +45,7 @@ async def async_setup_entry( data = hass.data[DOMAIN][entry.entry_id] hub: BondHub = data[HUB] bpup_subs: BPUPSubscriptions = data[BPUP_SUBS] + platform = entity_platform.async_get_current_platform() fans: list[Entity] = [ BondFan(hub, device, bpup_subs) @@ -47,6 +53,12 @@ async def async_setup_entry( 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) @@ -128,6 +140,41 @@ class BondFan(BondEntity, FanEntity): 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( self, speed: str | None = None, diff --git a/homeassistant/components/bond/light.py b/homeassistant/components/bond/light.py index 9fe33e8e99e..c47147a5648 100644 --- a/homeassistant/components/bond/light.py +++ b/homeassistant/components/bond/light.py @@ -4,7 +4,9 @@ from __future__ import annotations import logging from typing import Any +from aiohttp.client_exceptions import ClientResponseError from bond_api import Action, BPUPSubscriptions, DeviceType +import voluptuous as vol from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -14,12 +16,19 @@ from homeassistant.components.light import ( from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback 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_platform import AddEntitiesCallback 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 .utils import BondDevice @@ -45,6 +54,7 @@ async def async_setup_entry( data = hass.data[DOMAIN][entry.entry_id] hub: BondHub = data[HUB] bpup_subs: BPUPSubscriptions = data[BPUP_SUBS] + platform = entity_platform.async_get_current_platform() platform = entity_platform.async_get_current_platform() for service in ENTITY_SERVICES: @@ -92,6 +102,22 @@ async def async_setup_entry( 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( fan_lights + fan_up_lights + fan_down_lights + fireplaces + fp_lights + lights, True, @@ -103,6 +129,34 @@ class BondBaseLight(BondEntity, LightEntity): _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): """Representation of a Bond light.""" @@ -231,3 +285,31 @@ class BondFireplace(BondEntity, LightEntity): _LOGGER.debug("Fireplace async_turn_off called with: %s", kwargs) 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 diff --git a/homeassistant/components/bond/manifest.json b/homeassistant/components/bond/manifest.json index 3995ecf5024..7d1486b2e8f 100644 --- a/homeassistant/components/bond/manifest.json +++ b/homeassistant/components/bond/manifest.json @@ -3,9 +3,9 @@ "name": "Bond", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bond", - "requirements": ["bond-api==0.1.12"], + "requirements": ["bond-api==0.1.13"], "zeroconf": ["_bond._tcp.local."], - "codeowners": ["@prystupa"], + "codeowners": ["@prystupa", "@joshs85"], "quality_scale": "platinum", "iot_class": "local_push" } diff --git a/homeassistant/components/bond/services.yaml b/homeassistant/components/bond/services.yaml index 1cb24c5ed71..32a3e882739 100644 --- a/homeassistant/components/bond/services.yaml +++ b/homeassistant/components/bond/services.yaml @@ -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: name: Start increasing brightness description: "Start increasing the brightness of the light." @@ -21,3 +115,4 @@ stop: entity: integration: bond domain: light + diff --git a/homeassistant/components/bond/switch.py b/homeassistant/components/bond/switch.py index 0bb58946f0f..b493ac07945 100644 --- a/homeassistant/components/bond/switch.py +++ b/homeassistant/components/bond/switch.py @@ -3,15 +3,19 @@ from __future__ import annotations from typing import Any +from aiohttp.client_exceptions import ClientResponseError from bond_api import Action, BPUPSubscriptions, DeviceType +import voluptuous as vol from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry 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_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 .utils import BondHub @@ -25,6 +29,7 @@ async def async_setup_entry( data = hass.data[DOMAIN][entry.entry_id] hub: BondHub = data[HUB] bpup_subs: BPUPSubscriptions = data[BPUP_SUBS] + platform = entity_platform.async_get_current_platform() switches: list[Entity] = [ BondSwitch(hub, device, bpup_subs) @@ -32,6 +37,12 @@ async def async_setup_entry( 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) @@ -48,3 +59,14 @@ class BondSwitch(BondEntity, SwitchEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device 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 diff --git a/requirements_all.txt b/requirements_all.txt index 4d8230c8bee..788698ccf66 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -406,7 +406,7 @@ blockchain==1.4.4 # bme680==1.0.5 # homeassistant.components.bond -bond-api==0.1.12 +bond-api==0.1.13 # homeassistant.components.bosch_shc boschshcpy==0.2.19 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 47598fde7f8..bc673eb2d33 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -239,7 +239,7 @@ blebox_uniapi==1.3.3 blinkpy==0.17.0 # homeassistant.components.bond -bond-api==0.1.12 +bond-api==0.1.13 # homeassistant.components.bosch_shc boschshcpy==0.2.19 diff --git a/tests/components/bond/common.py b/tests/components/bond/common.py index 0791d002fed..0400b466e34 100644 --- a/tests/components/bond/common.py +++ b/tests/components/bond/common.py @@ -7,6 +7,8 @@ from datetime import timedelta from typing import Any from unittest.mock import MagicMock, patch +from aiohttp.client_exceptions import ClientResponseError + from homeassistant import core from homeassistant.components.bond.const import DOMAIN as BOND_DOMAIN 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") +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): """Patch Bond API device properties endpoint.""" if return_value is None: diff --git a/tests/components/bond/test_fan.py b/tests/components/bond/test_fan.py index bd5994f5182..e975f586ff4 100644 --- a/tests/components/bond/test_fan.py +++ b/tests/components/bond/test_fan.py @@ -4,9 +4,14 @@ from __future__ import annotations from datetime import timedelta from bond_api import Action, DeviceType, Direction +import pytest from homeassistant import core 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 ( ATTR_DIRECTION, ATTR_SPEED, @@ -19,6 +24,7 @@ from homeassistant.components.fan import ( SPEED_OFF, ) 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.entity_registry import EntityRegistry from homeassistant.util import utcnow @@ -26,6 +32,7 @@ from homeassistant.util import utcnow from .common import ( help_test_entity_available, patch_bond_action, + patch_bond_action_returns_clientresponseerror, patch_bond_device_state, 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()) +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): """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")) diff --git a/tests/components/bond/test_light.py b/tests/components/bond/test_light.py index 545feee21a5..3b846f3d996 100644 --- a/tests/components/bond/test_light.py +++ b/tests/components/bond/test_light.py @@ -5,7 +5,12 @@ from bond_api import Action, DeviceType import pytest 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 ( SERVICE_START_DECREASING_BRIGHTNESS, SERVICE_START_INCREASING_BRIGHTNESS, @@ -31,6 +36,7 @@ from homeassistant.util import utcnow from .common import ( help_test_entity_available, patch_bond_action, + patch_bond_action_returns_clientresponseerror, patch_bond_device_state, 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): """Create a ceiling fan (that has built-in light) with given name.""" 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): """Create a light that can only increase or decrease brightness.""" return { @@ -254,6 +284,270 @@ async def test_no_trust_state(hass: core.HomeAssistant): 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): """Tests a light that can only increase or decrease brightness delegates to API can start increasing brightness.""" await setup_platform( diff --git a/tests/components/bond/test_switch.py b/tests/components/bond/test_switch.py index 94a9179d3a7..f2ed6e9c3b5 100644 --- a/tests/components/bond/test_switch.py +++ b/tests/components/bond/test_switch.py @@ -2,10 +2,17 @@ from datetime import timedelta from bond_api import Action, DeviceType +import pytest 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.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.entity_registry import EntityRegistry from homeassistant.util import utcnow @@ -13,6 +20,7 @@ from homeassistant.util import utcnow from .common import ( help_test_entity_available, patch_bond_action, + patch_bond_action_returns_clientresponseerror, patch_bond_device_state, 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()) +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): """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"))