Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions homeassistant/components/fan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,21 +422,21 @@ def is_on(self):
return self.speed not in [SPEED_OFF, None]

@property
def _implemented_percentage(self):
def _implemented_percentage(self) -> bool:
"""Return true if percentage has been implemented."""
return not hasattr(self.set_percentage, _FAN_NATIVE) or not hasattr(
self.async_set_percentage, _FAN_NATIVE
)

@property
def _implemented_preset_mode(self):
def _implemented_preset_mode(self) -> bool:
"""Return true if preset_mode has been implemented."""
return not hasattr(self.set_preset_mode, _FAN_NATIVE) or not hasattr(
self.async_set_preset_mode, _FAN_NATIVE
)

@property
def _implemented_speed(self):
def _implemented_speed(self) -> bool:
"""Return true if speed has been implemented."""
return not hasattr(self.set_speed, _FAN_NATIVE) or not hasattr(
self.async_set_speed, _FAN_NATIVE
Expand Down
7 changes: 6 additions & 1 deletion homeassistant/components/template/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ def __init__(
# List of valid preset modes
self._preset_modes = preset_modes

@property
def _implemented_speed(self):
"""Return true if speed has been implemented."""
return bool(self._set_speed_script or self._speed_template)

@property
def name(self):
"""Return the display name of this fan."""
Expand All @@ -290,7 +295,7 @@ def supported_features(self) -> int:
@property
def speed_count(self) -> int:
"""Return the number of speeds the fan supports."""
return self._speed_count or super().speed_count
return self._speed_count or 100

@property
def speed_list(self) -> list:
Expand Down
261 changes: 258 additions & 3 deletions tests/components/template/test_fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,8 @@ async def test_set_percentage(hass, calls):


async def test_increase_decrease_speed(hass, calls):
"""Test set valid increase and derease speed."""
await _register_components(hass)
"""Test set valid increase and decrease speed."""
await _register_components(hass, speed_count=3)

# Turn on fan
await common.async_turn_on(hass, _TEST_FAN)
Expand Down Expand Up @@ -693,6 +693,42 @@ async def test_increase_decrease_speed(hass, calls):
_verify(hass, STATE_ON, SPEED_LOW, 33, None, None, None)


async def test_increase_decrease_speed_default_speed_count(hass, calls):
"""Test set valid increase and decrease speed."""
await _register_components(
hass,
)

# Turn on fan
await common.async_turn_on(hass, _TEST_FAN)

# Set fan's percentage speed to 100
await common.async_set_percentage(hass, _TEST_FAN, 100)

# verify
assert int(float(hass.states.get(_PERCENTAGE_INPUT_NUMBER).state)) == 100

_verify(hass, STATE_ON, SPEED_HIGH, 100, None, None, None)

# Set fan's percentage speed to 99
await common.async_decrease_speed(hass, _TEST_FAN)
assert int(float(hass.states.get(_PERCENTAGE_INPUT_NUMBER).state)) == 99

_verify(hass, STATE_ON, SPEED_HIGH, 99, None, None, None)

# Set fan's percentage speed to 98
await common.async_decrease_speed(hass, _TEST_FAN)
assert int(float(hass.states.get(_PERCENTAGE_INPUT_NUMBER).state)) == 98

_verify(hass, STATE_ON, SPEED_HIGH, 98, None, None, None)

for _ in range(32):
await common.async_decrease_speed(hass, _TEST_FAN)
assert int(float(hass.states.get(_PERCENTAGE_INPUT_NUMBER).state)) == 66

_verify(hass, STATE_ON, SPEED_MEDIUM, 66, None, None, None)


async def test_set_invalid_speed_from_initial_stage(hass, calls):
"""Test set invalid speed when fan is in initial state."""
await _register_components(hass)
Expand Down Expand Up @@ -926,7 +962,9 @@ def _verify(
assert attributes.get(ATTR_PRESET_MODE) == expected_preset_mode


async def _register_components(hass, speed_list=None, preset_modes=None):
async def _register_components(
hass, speed_list=None, preset_modes=None, speed_count=None
):
"""Register basic components for testing."""
with assert_setup_component(1, "input_boolean"):
assert await setup.async_setup_component(
Expand Down Expand Up @@ -1051,6 +1089,9 @@ async def _register_components(hass, speed_list=None, preset_modes=None):
if preset_modes:
test_fan_config["preset_modes"] = preset_modes

if speed_count:
test_fan_config["speed_count"] = speed_count

assert await setup.async_setup_component(
hass,
"fan",
Expand Down Expand Up @@ -1105,3 +1146,217 @@ async def test_unique_id(hass):
await hass.async_block_till_done()

assert len(hass.states.async_all()) == 1


@pytest.mark.parametrize(
"speed_count, percentage_step", [(0, 1), (100, 1), (3, 100 / 3)]
)
async def test_implemented_percentage(hass, speed_count, percentage_step):
"""Test a fan that implements percentage."""
await setup.async_setup_component(
hass,
"fan",
{
"fan": {
"platform": "template",
"fans": {
"mechanical_ventilation": {
"friendly_name": "Mechanische ventilatie",
"unique_id": "a2fd2e38-674b-4b47-b5ef-cc2362211a72",
"value_template": "{{ states('light.mv_snelheid') }}",
"percentage_template": "{{ (state_attr('light.mv_snelheid','brightness') | int / 255 * 100) | int }}",
"turn_on": [
{
"service": "switch.turn_off",
"target": {
"entity_id": "switch.mv_automatisch",
},
},
{
"service": "light.turn_on",
"target": {
"entity_id": "light.mv_snelheid",
},
"data": {"brightness_pct": 40},
},
],
"turn_off": [
{
"service": "light.turn_off",
"target": {
"entity_id": "light.mv_snelheid",
},
},
{
"service": "switch.turn_on",
"target": {
"entity_id": "switch.mv_automatisch",
},
},
],
"set_percentage": [
{
"service": "light.turn_on",
"target": {
"entity_id": "light.mv_snelheid",
},
"data": {"brightness_pct": "{{ percentage }}"},
}
],
"speed_count": speed_count,
},
},
},
},
)

await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()

assert len(hass.states.async_all()) == 1

state = hass.states.get("fan.mechanical_ventilation")
attributes = state.attributes
assert attributes["percentage_step"] == percentage_step


async def test_implemented_preset_mode(hass):
"""Test a fan that implements preset_mode."""
await setup.async_setup_component(
hass,
"fan",
{
"fan": {
"platform": "template",
"fans": {
"mechanical_ventilation": {
"friendly_name": "Mechanische ventilatie",
"unique_id": "a2fd2e38-674b-4b47-b5ef-cc2362211a72",
"value_template": "{{ states('light.mv_snelheid') }}",
"preset_mode_template": "{{ 'any' }}",
"preset_modes": ["any"],
"set_preset_mode": [
{
"service": "light.turn_on",
"target": {
"entity_id": "light.mv_snelheid",
},
"data": {"brightness_pct": "{{ percentage }}"},
}
],
"turn_on": [
{
"service": "switch.turn_off",
"target": {
"entity_id": "switch.mv_automatisch",
},
},
{
"service": "light.turn_on",
"target": {
"entity_id": "light.mv_snelheid",
},
"data": {"brightness_pct": 40},
},
],
"turn_off": [
{
"service": "light.turn_off",
"target": {
"entity_id": "light.mv_snelheid",
},
},
{
"service": "switch.turn_on",
"target": {
"entity_id": "switch.mv_automatisch",
},
},
],
},
},
},
},
)

await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()

assert len(hass.states.async_all()) == 1

state = hass.states.get("fan.mechanical_ventilation")
attributes = state.attributes
assert attributes["percentage"] is None


async def test_implemented_speed(hass):
"""Test a fan that implements speed."""
await setup.async_setup_component(
hass,
"fan",
{
"fan": {
"platform": "template",
"fans": {
"mechanical_ventilation": {
"friendly_name": "Mechanische ventilatie",
"unique_id": "a2fd2e38-674b-4b47-b5ef-cc2362211a72",
"value_template": "{{ states('light.mv_snelheid') }}",
"speed_template": "{{ 'fast' }}",
"speeds": ["slow", "fast"],
"set_preset_mode": [
{
"service": "light.turn_on",
"target": {
"entity_id": "light.mv_snelheid",
},
"data": {"brightness_pct": "{{ percentage }}"},
}
],
"turn_on": [
{
"service": "switch.turn_off",
"target": {
"entity_id": "switch.mv_automatisch",
},
},
{
"service": "light.turn_on",
"target": {
"entity_id": "light.mv_snelheid",
},
"data": {"brightness_pct": 40},
},
],
"turn_off": [
{
"service": "light.turn_off",
"target": {
"entity_id": "light.mv_snelheid",
},
},
{
"service": "switch.turn_on",
"target": {
"entity_id": "switch.mv_automatisch",
},
},
],
},
},
},
},
)

await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()

assert len(hass.states.async_all()) == 1

state = hass.states.get("fan.mechanical_ventilation")
attributes = state.attributes
assert attributes["percentage"] == 100
assert attributes["speed"] == "fast"