async def test_hold(
    sut: LightController,
    monkeypatch: MonkeyPatch,
    mocker: MockerFixture,
    attribute_input: str,
    direction_input: str,
    previous_direction: str,
    light_state: Literal["on", "off"],
    smooth_power_on: bool,
    expected_calls: int,
    expected_direction: str,
):
    value_attribute = 10
    monkeypatch.setattr(sut, "get_entity_state",
                        fake_fn(to_return=light_state, async_=True))
    monkeypatch.setattr(sut, "get_value_attribute",
                        fake_fn(to_return=value_attribute, async_=True))
    monkeypatch.setattr(sut, "get_attribute",
                        fake_fn(to_return=attribute_input, async_=True))
    sut.smooth_power_on = smooth_power_on
    sut.feature_support._supported_features = 0
    stepper = MinMaxStepper(1, 10, 10)
    stepper.previous_direction = previous_direction
    sut.automatic_steppers = {attribute_input: stepper}
    super_hold_patch = mocker.patch.object(ReleaseHoldController, "hold")

    await sut.hold(attribute_input, direction_input)

    assert super_hold_patch.call_count == expected_calls
    if expected_calls > 0:
        super_hold_patch.assert_called_with(attribute_input,
                                            expected_direction)
Exemple #2
0
def test_minmax_stepper_step(
    minmax, value, steps, direction, expected_value, expected_exceeded
):
    stepper = MinMaxStepper(*minmax, steps)

    # SUT
    new_value, exceeded = stepper.step(value, direction)

    # Checks
    assert new_value == expected_value
    assert exceeded == expected_exceeded
Exemple #3
0
    async def initialize(self) -> None:
        self.media_player = self.args["media_player"]
        await self.check_domain(self.media_player)
        volume_steps = self.args.get("volume_steps", DEFAULT_VOLUME_STEPS)
        update_supported_features = self.args.get("update_supported_features",
                                                  False)
        self.volume_stepper = MinMaxStepper(0, 1, volume_steps)
        self.volume_level = 0.0

        self.supported_features = MediaPlayerSupport(
            self.media_player, self, update_supported_features)
        await super().initialize()
Exemple #4
0
def test_minmax_stepper_step(
    minmax: Tuple[int, int],
    value: int,
    steps: int,
    direction: Literal["up", "down"],
    expected_value: int,
    expected_exceeded: bool,
):
    stepper = MinMaxStepper(*minmax, steps)

    new_value, exceeded = stepper.step(value, direction)

    assert new_value == expected_value
    assert exceeded == expected_exceeded
async def test_click(
    sut: LightController,
    monkeypatch: MonkeyPatch,
    mocker: MockerFixture,
    attribute_input: str,
    direction_input: Literal["up", "down"],
    light_state: Literal["on", "off"],
    smooth_power_on: bool,
    expected_calls: int,
):
    value_attribute = 10
    monkeypatch.setattr(sut, "get_entity_state",
                        fake_fn(to_return=light_state, async_=True))
    monkeypatch.setattr(sut, "get_value_attribute",
                        fake_fn(to_return=value_attribute, async_=True))
    monkeypatch.setattr(sut, "get_attribute",
                        fake_fn(to_return=attribute_input, async_=True))
    change_light_state_patch = mocker.patch.object(sut, "change_light_state")
    sut.smooth_power_on = smooth_power_on
    sut.feature_support._supported_features = 0
    stepper = MinMaxStepper(1, 10, 10)
    sut.manual_steppers = {attribute_input: stepper}

    await sut.click(attribute_input, direction_input)

    assert change_light_state_patch.call_count == expected_calls
async def test_click(
    sut,
    monkeypatch,
    mocker,
    attribute_input,
    direction_input,
    light_state,
    smooth_power_on,
    expected_calls,
):
    value_attribute = 10

    async def fake_get_entity_state(*args, **kwargs):
        return light_state

    async def fake_get_value_attribute(*args, **kwargs):
        return value_attribute

    async def fake_get_attribute(*args, **kwargs):
        return attribute_input

    monkeypatch.setattr(sut, "get_entity_state", fake_get_entity_state)
    monkeypatch.setattr(sut, "get_value_attribute", fake_get_value_attribute)
    monkeypatch.setattr(sut, "get_attribute", fake_get_attribute)
    change_light_state_patch = mocker.patch.object(sut, "change_light_state")
    sut.smooth_power_on = smooth_power_on
    stepper = MinMaxStepper(1, 10, 10)
    sut.manual_steppers = {attribute_input: stepper}

    # SUT
    await sut.click(attribute_input, direction_input)

    # Checks
    assert change_light_state_patch.call_count == expected_calls
Exemple #7
0
def test_minmax_stepper_get_direction(
    minmax: Tuple[int, int],
    value: int,
    direction: str,
    previous_direction: str,
    expected_direction: str,
    expected_new_previous_direction: str,
):
    stepper = MinMaxStepper(*minmax, 10)
    stepper.previous_direction = previous_direction

    # SUT
    new_direction = stepper.get_direction(value, direction)

    # Checks
    assert new_direction == expected_direction
    assert stepper.previous_direction == expected_new_previous_direction
Exemple #8
0
async def test_on_full(sut: LightController, mocker: MockerFixture):
    attribute = "test_attribute"
    max_ = 10
    on_patch = mocker.patch.object(sut, "on")
    stepper = MinMaxStepper(1, max_, 10)
    sut.automatic_steppers = {attribute: stepper}

    await sut.on_full(attribute, light_on=False)

    on_patch.assert_called_once_with(light_on=False, **{attribute: max_})
async def test_on_min(sut: LightController, mocker: MockerFixture):
    attribute = "test_attribute"
    min_ = 1
    on_patch = mocker.patch.object(sut, "_on")
    stepper = MinMaxStepper(min_, 10, 10)
    sut.automatic_steppers = {attribute: stepper}

    await sut.on_min(attribute)

    on_patch.assert_called_once_with(**{attribute: min_})
async def test_on_min(sut, mocker):
    attribute = "test_attribute"
    min_ = 1
    on_patch = mocker.patch.object(sut, "on")
    stepper = MinMaxStepper(min_, 10, 10)
    sut.automatic_steppers = {attribute: stepper}

    # SUT
    await sut.on_min(attribute, light_on=False)

    # Checks
    on_patch.assert_called_once_with(light_on=False, **{attribute: min_})
async def test_hold(
    sut,
    monkeypatch,
    mocker,
    attribute_input,
    direction_input,
    previous_direction,
    light_state,
    smooth_power_on,
    expected_calls,
    expected_direction,
):
    value_attribute = 10

    async def fake_get_entity_state(*args, **kwargs):
        return light_state

    async def fake_get_value_attribute(*args, **kwargs):
        return value_attribute

    async def fake_get_attribute(*args, **kwargs):
        return attribute_input

    monkeypatch.setattr(sut, "get_entity_state", fake_get_entity_state)
    monkeypatch.setattr(sut, "get_value_attribute", fake_get_value_attribute)
    monkeypatch.setattr(sut, "get_attribute", fake_get_attribute)
    sut.smooth_power_on = smooth_power_on
    stepper = MinMaxStepper(1, 10, 10)
    stepper.previous_direction = previous_direction
    sut.automatic_steppers = {attribute_input: stepper}
    super_hold_patch = mocker.patch.object(ReleaseHoldController, "hold")

    # SUT
    await sut.hold(attribute_input, direction_input)

    # Checks
    assert super_hold_patch.call_count == expected_calls
    if expected_calls > 0:
        super_hold_patch.assert_called_with(attribute_input,
                                            expected_direction)
    async def init(self) -> None:
        manual_steps = self.args.get("manual_steps", DEFAULT_MANUAL_STEPS)
        automatic_steps = self.args.get("automatic_steps",
                                        DEFAULT_AUTOMATIC_STEPS)
        self.min_brightness = self.args.get("min_brightness",
                                            DEFAULT_MIN_BRIGHTNESS)
        self.max_brightness = self.args.get("max_brightness",
                                            DEFAULT_MAX_BRIGHTNESS)
        self.min_white_value = self.args.get("min_white_value",
                                             DEFAULT_MIN_WHITE_VALUE)
        self.max_white_value = self.args.get("max_white_value",
                                             DEFAULT_MAX_WHITE_VALUE)
        self.min_color_temp = self.args.get("min_color_temp",
                                            DEFAULT_MIN_COLOR_TEMP)
        self.max_color_temp = self.args.get("max_color_temp",
                                            DEFAULT_MAX_COLOR_TEMP)
        self.transition = self.args.get("transition", DEFAULT_TRANSITION)
        self.color_wheel = get_color_wheel(
            self.args.get("color_wheel", "default_color_wheel"))

        color_stepper = CircularStepper(0,
                                        len(self.color_wheel) - 1,
                                        len(self.color_wheel))
        self.manual_steppers: Dict[str, Stepper] = {
            LightController.ATTRIBUTE_BRIGHTNESS:
            MinMaxStepper(self.min_brightness, self.max_brightness,
                          manual_steps),
            LightController.ATTRIBUTE_WHITE_VALUE:
            MinMaxStepper(self.min_white_value, self.max_white_value,
                          manual_steps),
            LightController.ATTRIBUTE_COLOR_TEMP:
            MinMaxStepper(self.min_color_temp, self.max_color_temp,
                          manual_steps),
            LightController.ATTRIBUTE_XY_COLOR:
            color_stepper,
        }
        self.automatic_steppers: Dict[str, Stepper] = {
            LightController.ATTRIBUTE_BRIGHTNESS:
            MinMaxStepper(self.min_brightness, self.max_brightness,
                          automatic_steps),
            LightController.ATTRIBUTE_WHITE_VALUE:
            MinMaxStepper(self.min_white_value, self.max_white_value,
                          automatic_steps),
            LightController.ATTRIBUTE_COLOR_TEMP:
            MinMaxStepper(self.min_color_temp, self.max_color_temp,
                          automatic_steps),
            LightController.ATTRIBUTE_XY_COLOR:
            color_stepper,
        }
        self.smooth_power_on = self.args.get("smooth_power_on",
                                             self.supports_smooth_power_on())
        self.add_transition = self.args.get("add_transition",
                                            DEFAULT_ADD_TRANSITION)
        self.add_transition_turn_toggle = self.args.get(
            "add_transition_turn_toggle", DEFAULT_TRANSITION_TURN_TOGGLE)
        await super().init()
    async def initialize(self) -> None:
        self.light = self.get_light(self.args["light"])
        await self.check_domain(self.light["name"])
        manual_steps = self.args.get("manual_steps", DEFAULT_MANUAL_STEPS)
        automatic_steps = self.args.get("automatic_steps", DEFAULT_AUTOMATIC_STEPS)
        self.min_brightness = self.args.get("min_brightness", DEFAULT_MIN_BRIGHTNESS)
        self.max_brightness = self.args.get("max_brightness", DEFAULT_MAX_BRIGHTNESS)
        self.min_white_value = self.args.get("min_white_value", DEFAULT_MIN_WHITE_VALUE)
        self.max_white_value = self.args.get("max_white_value", DEFAULT_MAX_WHITE_VALUE)
        self.min_color_temp = self.args.get("min_color_temp", DEFAULT_MIN_COLOR_TEMP)
        self.max_color_temp = self.args.get("max_color_temp", DEFAULT_MAX_COLOR_TEMP)
        self.transition = self.args.get("transition", DEFAULT_TRANSITION)
        self.color_wheel = get_color_wheel(
            self.args.get("color_wheel", "default_color_wheel")
        )

        color_stepper = CircularStepper(
            0, len(self.color_wheel) - 1, len(self.color_wheel)
        )
        self.manual_steppers: Dict[str, Stepper] = {
            LightController.ATTRIBUTE_BRIGHTNESS: MinMaxStepper(
                self.min_brightness, self.max_brightness, manual_steps
            ),
            LightController.ATTRIBUTE_WHITE_VALUE: MinMaxStepper(
                self.min_white_value, self.max_white_value, manual_steps
            ),
            LightController.ATTRIBUTE_COLOR_TEMP: MinMaxStepper(
                self.min_color_temp, self.max_color_temp, manual_steps
            ),
            LightController.ATTRIBUTE_XY_COLOR: color_stepper,
        }
        self.automatic_steppers: Dict[str, Stepper] = {
            LightController.ATTRIBUTE_BRIGHTNESS: MinMaxStepper(
                self.min_brightness, self.max_brightness, automatic_steps
            ),
            LightController.ATTRIBUTE_WHITE_VALUE: MinMaxStepper(
                self.min_white_value, self.max_white_value, automatic_steps
            ),
            LightController.ATTRIBUTE_COLOR_TEMP: MinMaxStepper(
                self.min_color_temp, self.max_color_temp, automatic_steps
            ),
            LightController.ATTRIBUTE_XY_COLOR: color_stepper,
        }
        self.smooth_power_on = self.args.get(
            "smooth_power_on", self.supports_smooth_power_on()
        )
        self.add_transition = self.args.get("add_transition", True)
        self.add_transition_turn_toggle = self.args.get(
            "add_transition_turn_toggle", False
        )
        update_supported_features = self.args.get("update_supported_features", False)

        self.supported_features = LightSupport(
            self.light["name"], self, update_supported_features
        )
        await super().initialize()
async def test_hold_loop(sut, mocker, value_attribute):
    attribute = "test_attribute"
    direction = Stepper.UP
    sut.value_attribute = value_attribute
    change_light_state_patch = mocker.patch.object(sut, "change_light_state")
    stepper = MinMaxStepper(1, 10, 10)
    sut.automatic_steppers = {attribute: stepper}

    # SUT
    exceeded = await sut.hold_loop(attribute, direction)

    if value_attribute is None:
        assert exceeded
    else:
        change_light_state_patch.assert_called_once_with(
            sut.value_attribute, attribute, direction, stepper, "hold")
async def test_hold_loop(sut: LightController, mocker: MockerFixture,
                         value_attribute: int):
    attribute = "test_attribute"
    direction = Stepper.UP
    sut.smooth_power_on_check = False
    sut.value_attribute = value_attribute
    change_light_state_patch = mocker.patch.object(sut, "change_light_state")
    stepper = MinMaxStepper(1, 10, 10)
    sut.automatic_steppers = {attribute: stepper}

    exceeded = await sut.hold_loop(attribute, direction)

    if value_attribute is None:
        assert exceeded
    else:
        change_light_state_patch.assert_called_once_with(
            sut.value_attribute, attribute, direction, stepper, "hold")
    else:
        output = await sut.get_value_attribute(attribute_input,
                                               direction_input)

        # Checks
        assert output == float(expected_output)


@pytest.mark.parametrize(
    "old, attribute, direction, stepper, light_state, smooth_power_on, expected_stop, expected_value_attribute",
    [
        (
            50,
            LightController.ATTRIBUTE_BRIGHTNESS,
            Stepper.UP,
            MinMaxStepper(1, 255, 254),
            "on",
            False,
            False,
            51,
        ),
        (
            0,
            "xy_color",
            Stepper.UP,
            CircularStepper(0, 30, 30),
            "on",
            False,
            False,
            0,
        ),
class MediaPlayerController(TypeController[Entity], ReleaseHoldController):

    domains = ["media_player"]
    entity_arg = "media_player"

    async def initialize(self) -> None:
        volume_steps = self.args.get("volume_steps", DEFAULT_VOLUME_STEPS)
        self.volume_stepper = MinMaxStepper(0, 1, volume_steps)
        self.volume_level = 0.0
        await super().initialize()

    def _get_entity_type(self) -> Type[Entity]:
        return Entity

    def get_type_actions_mapping(self) -> TypeActionsMapping:
        return {
            MediaPlayer.HOLD_VOLUME_DOWN: (self.hold, Stepper.DOWN),
            MediaPlayer.HOLD_VOLUME_UP: (self.hold, Stepper.UP),
            MediaPlayer.CLICK_VOLUME_DOWN: self.volume_down,
            MediaPlayer.CLICK_VOLUME_UP: self.volume_up,
            MediaPlayer.RELEASE: self.release,
            MediaPlayer.PLAY: self.play,
            MediaPlayer.PAUSE: self.pause,
            MediaPlayer.PLAY_PAUSE: self.play_pause,
            MediaPlayer.NEXT_TRACK: self.next_track,
            MediaPlayer.PREVIOUS_TRACK: self.previous_track,
            MediaPlayer.NEXT_SOURCE: (self.change_source_list, Stepper.UP),
            MediaPlayer.PREVIOUS_SOURCE: (self.change_source_list, Stepper.DOWN),
        }

    @action
    async def change_source_list(self, direction: str) -> None:
        entity_states = await self.get_entity_state(self.entity.name, attribute="all")
        entity_attributes = entity_states["attributes"]
        source_list = entity_attributes.get("source_list")
        if len(source_list) == 0 or source_list is None:
            self.log(
                f"⚠️ There is no `source_list` parameter in `{self.entity.name}`",
                level="WARNING",
                ascii_encode=False,
            )
            return
        source = entity_attributes.get("source")
        if source is None:
            new_index_source = 0
        else:
            index_source = source_list.index(source)
            source_stepper = CircularStepper(0, len(source_list) - 1, len(source_list))
            new_index_source, _ = source_stepper.step(index_source, direction)
        await self.call_service(
            "media_player/select_source",
            entity_id=self.entity.name,
            source=source_list[new_index_source],
        )

    @action
    async def play(self) -> None:
        await self.call_service("media_player/media_play", entity_id=self.entity.name)

    @action
    async def pause(self) -> None:
        await self.call_service("media_player/media_pause", entity_id=self.entity.name)

    @action
    async def play_pause(self) -> None:
        await self.call_service(
            "media_player/media_play_pause", entity_id=self.entity.name
        )

    @action
    async def previous_track(self) -> None:
        await self.call_service(
            "media_player/media_previous_track", entity_id=self.entity.name
        )

    @action
    async def next_track(self) -> None:
        await self.call_service(
            "media_player/media_next_track", entity_id=self.entity.name
        )

    @action
    async def volume_up(self) -> None:
        await self.prepare_volume_change()
        await self.volume_change(Stepper.UP)

    @action
    async def volume_down(self) -> None:
        await self.prepare_volume_change()
        await self.volume_change(Stepper.DOWN)

    @action
    async def hold(self, direction: str) -> None:
        await self.prepare_volume_change()
        await super().hold(direction)

    async def prepare_volume_change(self) -> None:
        volume_level = await self.get_entity_state(
            self.entity.name, attribute="volume_level"
        )
        if volume_level is not None:
            self.volume_level = volume_level

    async def volume_change(self, direction: str) -> bool:
        if await self.feature_support.is_supported(MediaPlayerSupport.VOLUME_SET):
            self.volume_level, exceeded = self.volume_stepper.step(
                self.volume_level, direction
            )
            await self.call_service(
                "media_player/volume_set",
                entity_id=self.entity.name,
                volume_level=self.volume_level,
            )
            return exceeded
        else:
            if direction == Stepper.UP:
                await self.call_service(
                    "media_player/volume_up", entity_id=self.entity.name
                )
            else:
                await self.call_service(
                    "media_player/volume_down", entity_id=self.entity.name
                )
            return False

    async def hold_loop(self, direction: str) -> bool:  # type: ignore
        return await self.volume_change(direction)

    def default_delay(self) -> int:
        return 500
    with wrap_exetuction(error_expected=error_expected, exception=ValueError):
        output = await sut.get_value_attribute(attribute_input)

    if not error_expected:
        assert output == float(expected_output)


@pytest.mark.parametrize(
    "old, attribute, direction, stepper, smooth_power_on_check, stop_expected, expected_value_attribute",
    [
        (
            50,
            LightController.ATTRIBUTE_BRIGHTNESS,
            Stepper.UP,
            MinMaxStepper(1, 255, 254),
            False,
            False,
            51,
        ),
        (0, "xy_color", Stepper.UP, CircularStepper(0, 30,
                                                    30), False, False, 0),
        (
            499,
            "color_temp",
            Stepper.UP,
            MinMaxStepper(153, 500, 10),
            False,
            True,
            500,
        ),
 async def initialize(self) -> None:
     volume_steps = self.args.get("volume_steps", DEFAULT_VOLUME_STEPS)
     self.volume_stepper = MinMaxStepper(0, 1, volume_steps)
     self.volume_level = 0.0
     await super().initialize()