"""VeSync Humidifier Devices."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import orjson
from typing_extensions import deprecated

from pyvesync.base_devices.humidifier_base import BreathingLampState, VeSyncHumidifier
from pyvesync.const import ConnectionStatus, DeviceStatus, DryingModes
from pyvesync.models import humidifier_models as models
from pyvesync.models.bypass_models import (
    ResultV2GetTimer,
    ResultV2GetTimerV2,
    ResultV2SetTimer,
)
from pyvesync.utils.device_mixins import BypassV2Mixin, process_bypassv2_result
from pyvesync.utils.helpers import Helpers, Timer, Validators

if TYPE_CHECKING:
    from pyvesync import VeSync
    from pyvesync.device_map import HumidifierMap
    from pyvesync.models.vesync_models import ResponseDeviceDetailsModel


logger = logging.getLogger(__name__)


class VeSyncHumid200300S(BypassV2Mixin, VeSyncHumidifier):
    """300S Humidifier Class.

    Primary class for VeSync humidifier devices.

    Args:
        details (ResponseDeviceDetailsModel): The device details.
        manager (VeSync): The manager object for API calls.
        feature_map (HumidifierMap): The feature map for the device.

    Attributes:
        state (HumidifierState): The state of the humidifier.
        last_response (ResponseInfo): Last response info from API call.
        manager (VeSync): Manager object for API calls.
        device_name (str): Name of device.
        device_image (str): URL for device image.
        cid (str): Device ID.
        connection_type (str): Connection type of device.
        device_type (str): Type of device.
        type (str): Type of device.
        uuid (str): UUID of device, not always present.
        config_module (str): Configuration module of device.
        mac_id (str): MAC ID of device.
        current_firm_version (str): Current firmware version of device.
        device_region (str): Region of device. (US, EU, etc.)
        pid (str): Product ID of device, pulled by some devices on update.
        sub_device_no (int): Sub-device number of device.
        product_type (str): Product type of device.
        features (dict): Features of device.
        mist_levels (list): List of mist levels.
        mist_modes (list): List of mist modes.
        target_minmax (tuple): Tuple of target min and max values.
        warm_mist_levels (list): List of warm mist levels.
    """

    __slots__ = ()

    def __init__(
        self,
        details: ResponseDeviceDetailsModel,
        manager: VeSync,
        feature_map: HumidifierMap,
    ) -> None:
        """Initialize 200S/300S Humidifier class."""
        super().__init__(details, manager, feature_map)

    def _set_state(self, resp_model: models.ClassicLVHumidResult) -> None:
        """Set state from get_details API model."""
        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.device_status = DeviceStatus.from_bool(resp_model.enabled)
        self.state.mode = self._reverse_mist_modes.get(resp_model.mode)
        self.state.humidity = resp_model.humidity
        self.state.mist_virtual_level = resp_model.mist_virtual_level or 0
        self.state.mist_level = resp_model.mist_level or 0
        self.state.water_lacks = resp_model.water_lacks
        self.state.humidity_high = resp_model.humidity_high
        self.state.water_tank_lifted = resp_model.water_tank_lifted
        self.state.auto_stop_target_reached = resp_model.automatic_stop_reach_target
        if self.supports_nightlight and resp_model.night_light_brightness is not None:
            self.state.nightlight_brightness = resp_model.night_light_brightness
            self.state.nightlight_status = (
                DeviceStatus.ON
                if resp_model.night_light_brightness > 0
                else DeviceStatus.OFF
            )
        self.state.display_status = DeviceStatus.from_bool(resp_model.display)
        if self.supports_warm_mist and resp_model.warm_level is not None:
            self.state.warm_mist_level = resp_model.warm_level
            self.state.warm_mist_enabled = resp_model.warm_enabled
        config = resp_model.configuration
        if config is not None:
            self.state.auto_target_humidity = config.auto_target_humidity
            self.state.automatic_stop_config = config.automatic_stop
            self.state.display_set_status = DeviceStatus.from_bool(config.display)

    async def get_details(self) -> None:
        r_dict = await self.call_bypassv2_api('getHumidifierStatus')
        r_model = process_bypassv2_result(
            self, logger, 'get_details', r_dict, models.ClassicLVHumidResult
        )
        if r_model is None:
            return
        self._set_state(r_model)

    async def toggle_switch(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.device_status == DeviceStatus.ON

        payload_data = {'enabled': toggle, 'id': 0}
        r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
        if r is None:
            return False

        self.state.device_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def get_timer(self) -> Timer | None:
        r_dict = await self.call_bypassv2_api('getTimer')
        result_model = process_bypassv2_result(
            self, logger, 'get_timer', r_dict, ResultV2GetTimer
        )
        if result_model is None:
            return None
        if not result_model.timers:
            logger.debug('No timers found')
            return None
        timer = result_model.timers[0]
        self.state.timer = Timer(
            timer_duration=timer.total,
            action=timer.action,
            id=timer.id,
        )
        return self.state.timer

    async def clear_timer(self) -> bool:
        if self.state.timer is None:
            logger.debug('No timer to clear, run get_timer() first.')
            return False
        payload = {
            'id': self.state.timer.id,
        }
        r_dict = await self.call_bypassv2_api('delTimer', payload)
        r = Helpers.process_dev_response(logger, 'clear_timer', self, r_dict)
        if r is None:
            return False
        self.state.timer = None
        return True

    async def set_timer(self, duration: int, action: str | None = None) -> bool:
        if action is None:
            action = (
                DeviceStatus.OFF
                if self.state.device_status == DeviceStatus.ON
                else DeviceStatus.ON
            )
        payload_data = {
            'action': str(action),
            'total': duration,
        }
        r_dict = await self.call_bypassv2_api('addTimer', payload_data)
        r = process_bypassv2_result(self, logger, 'set_timer', r_dict, ResultV2SetTimer)
        if r is None:
            return False

        self.state.timer = Timer(
            timer_duration=duration, action=action, id=r.id, remaining=0
        )
        return True

    @deprecated('Use turn_on_automatic_stop() instead.')
    async def automatic_stop_on(self) -> bool:
        """Turn 200S/300S Humidifier automatic stop on."""
        return await self.toggle_automatic_stop(True)

    @deprecated('Use turn_off_automatic_stop() instead.')
    async def automatic_stop_off(self) -> bool:
        """Turn 200S/300S Humidifier automatic stop on."""
        return await self.toggle_automatic_stop(False)

    @deprecated('Use toggle_automatic_stop(toggle: bool) instead.')
    async def set_automatic_stop(self, mode: bool) -> bool:
        """Set 200S/300S Humidifier to automatic stop."""
        return await self.toggle_automatic_stop(mode)

    async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.automatic_stop_config is not True

        payload_data = {'enabled': toggle}
        r_dict = await self.call_bypassv2_api('setAutomaticStop', payload_data)
        r = Helpers.process_dev_response(logger, 'set_automatic_stop', self, r_dict)
        if r is None:
            return False

        self.state.automatic_stop_config = toggle
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    @deprecated('Use toggle_display(toggle: bool) instead.')
    async def set_display(self, toggle: bool) -> bool:
        """Deprecated method to toggle display on/off.

        Use toggle_display(toggle: bool) instead.
        """
        return await self.toggle_display(toggle)

    async def toggle_display(self, toggle: bool) -> bool:
        payload_data = {'state': toggle}
        r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
        r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
        if r is None:
            return False
        self.state.display_set_status = DeviceStatus.from_bool(toggle)
        self.state.display_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_humidity(self, humidity: int) -> bool:
        if not Validators.validate_range(humidity, *self.target_minmax):
            logger.warning(
                'Invalid humidity, must be between %s and %s', *self.target_minmax
            )
            return False

        payload_data = {'target_humidity': humidity}
        r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
        r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
        if r is None:
            return False
        self.state.auto_target_humidity = humidity
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_nightlight_brightness(self, brightness: int) -> bool:
        if not self.supports_nightlight:
            logger.warning(
                '%s is a %s does not have a nightlight or it is not supported.',
                self.device_name,
                self.device_type,
            )
            return False

        if not Validators.validate_zero_to_hundred(brightness):
            logger.warning('Brightness value must be set between 0 and 100')
            return False

        payload_data = {'night_light_brightness': brightness}
        r_dict = await self.call_bypassv2_api('setNightLightBrightness', payload_data)
        r = Helpers.process_dev_response(
            logger, 'set_night_light_brightness', self, r_dict
        )
        if r is None:
            return False
        self.state.nightlight_brightness = brightness
        self.state.nightlight_status = (
            DeviceStatus.ON if brightness > 0 else DeviceStatus.OFF
        )
        return True

    async def toggle_nightlight(self, toggle: bool | None = None) -> bool:
        if not self.supports_nightlight:
            logger.warning(
                '%s is a %s does not have a nightlight or it is not supported.',
                self.device_name,
                self.device_type,
            )
            return False

        if toggle is None:
            toggle = self.state.nightlight_status != DeviceStatus.ON
        brightness = 100 if toggle else 0
        return await self.set_nightlight_brightness(brightness)

    async def set_mode(self, mode: str) -> bool:
        if mode not in self.mist_modes:
            logger.warning('Invalid humidity mode used - %s', mode)
            logger.info(
                'Proper modes for this device are - %s',
                orjson.dumps(
                    self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
                ),
            )
            return False

        payload_data = {'mode': self.mist_modes[mode]}
        r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)
        r = Helpers.process_dev_response(logger, 'set_humidity_mode', self, r_dict)
        if r is None:
            return False

        self.state.mode = mode
        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_warm_level(self, warm_level: int) -> bool:
        if not self.supports_warm_mist:
            logger.debug(
                '%s is a %s does not have a mist warmer',
                self.device_name,
                self.device_type,
            )
            return False

        if warm_level not in self.warm_mist_levels:
            logger.warning('warm_level value must be - %s', str(self.warm_mist_levels))
            return False

        payload_data = {'type': 'warm', 'level': warm_level, 'id': 0}
        r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_warm_level', self, r_dict)
        if r is None:
            return False

        self.state.warm_mist_level = warm_level
        self.state.warm_mist_enabled = True
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mist_level(self, level: int) -> bool:
        if level not in self.mist_levels:
            logger.warning(
                'Humidifier mist level must be between %s and %s',
                self.mist_levels[0],
                self.mist_levels[-1],
            )
            return False

        payload_data = {'id': 0, 'level': level, 'type': 'mist'}
        r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
        if r is None:
            return False

        self.state.mist_virtual_level = level
        self.state.mist_level = level
        self.state.connection_status = ConnectionStatus.ONLINE
        return True


class VeSyncHumid200S(VeSyncHumid200300S):
    """Levoit Classic 200S Specific class.

    Overrides the `toggle_display(toggle: bool)` method of the
    VeSyncHumid200300S class.

    Args:
        details (ResponseDeviceDetailsModel): The device details.
        manager (VeSync): The manager object for API calls.
        feature_map (HumidifierMap): The feature map for the device.

    Attributes:
        state (HumidifierState): The state of the humidifier.
        last_response (ResponseInfo): Last response from API call.
        manager (VeSync): Manager object for API calls.
        device_name (str): Name of device.
        device_image (str): URL for device image.
        cid (str): Device ID.
        connection_type (str): Connection type of device.
        device_type (str): Type of device.
        type (str): Type of device.
        uuid (str): UUID of device, not always present.
        config_module (str): Configuration module of device.
        mac_id (str): MAC ID of device.
        current_firm_version (str): Current firmware version of device.
        device_region (str): Region of device. (US, EU, etc.)
        pid (str): Product ID of device, pulled by some devices on update.
        sub_device_no (int): Sub-device number of device.
        product_type (str): Product type of device.
        features (dict): Features of device.
        mist_levels (list): List of mist levels.
        mist_modes (list): List of mist modes.
        target_minmax (tuple): Tuple of target min and max values.
        warm_mist_levels (list): List of warm mist levels.
    """

    __slots__ = ()

    def __init__(
        self,
        details: ResponseDeviceDetailsModel,
        manager: VeSync,
        feature_map: HumidifierMap,
    ) -> None:
        """Initialize levoit 200S device class.

        This overrides the `toggle_display(toggle: bool)` method of the
        VeSyncHumid200300S class.
        """
        super().__init__(details, manager, feature_map)

    async def toggle_display(self, toggle: bool) -> bool:
        payload_data = {'enabled': toggle, 'id': 0}
        r_dict = await self.call_bypassv2_api('setIndicatorLightSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_display', self, r_dict)
        if r is None:
            return False

        self.state.display_set_status = DeviceStatus.from_bool(toggle)
        self.state.display_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True


class VeSyncSuperior6000S(BypassV2Mixin, VeSyncHumidifier):
    """Superior 6000S Humidifier.

    Args:
        details (ResponseDeviceDetailsModel): The device details.
        manager (VeSync): The manager object for API calls.
        feature_map (HumidifierMap): The feature map for the device.

    Attributes:
        state (HumidifierState): The state of the humidifier.
        last_response (ResponseInfo): Last response from API call.
        manager (VeSync): Manager object for API calls.
        device_name (str): Name of device.
        device_image (str): URL for device image.
        cid (str): Device ID.
        connection_type (str): Connection type of device.
        device_type (str): Type of device.
        type (str): Type of device.
        uuid (str): UUID of device, not always present.
        config_module (str): Configuration module of device.
        mac_id (str): MAC ID of device.
        current_firm_version (str): Current firmware version of device.
        device_region (str): Region of device. (US, EU, etc.)
        pid (str): Product ID of device, pulled by some devices on update.
        sub_device_no (int): Sub-device number of device.
        product_type (str): Product type of device.
        features (dict): Features of device.
        mist_levels (list): List of mist levels.
        mist_modes (list): List of mist modes.
        target_minmax (tuple): Tuple of target min and max values.
        warm_mist_levels (list): List of warm mist levels.
    """

    __slots__ = ()

    def __init__(
        self,
        details: ResponseDeviceDetailsModel,
        manager: VeSync,
        feature_map: HumidifierMap,
    ) -> None:
        """Initialize Superior 6000S Humidifier class."""
        super().__init__(details, manager, feature_map)

    def _set_state(self, resp_model: models.Superior6000SResult) -> None:
        """Set state from Superior 6000S API result model."""
        self.state.device_status = DeviceStatus.from_int(resp_model.powerSwitch)
        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.mode = self._reverse_mist_modes.get(resp_model.workMode)
        if self.state.mode is None:
            logger.warning('Unknown mist mode received: %s', resp_model.workMode)

        self.state.auto_target_humidity = resp_model.targetHumidity
        self.state.humidity = resp_model.humidity
        self.state.mist_level = resp_model.mistLevel
        self.state.mist_virtual_level = resp_model.virtualLevel
        self.state.water_lacks = bool(resp_model.waterLacksState)
        self.state.water_tank_lifted = bool(resp_model.waterTankLifted)
        self.state.automatic_stop_config = bool(resp_model.autoStopSwitch)
        self.state.auto_stop_target_reached = bool(resp_model.autoStopState)
        self.state.display_set_status = DeviceStatus.from_int(resp_model.screenState)
        self.state.display_status = DeviceStatus.from_int(resp_model.screenState)
        self.state.auto_preference = resp_model.autoPreference
        self.state.filter_life = resp_model.filterLifePercent
        self.state.child_lock = bool(resp_model.childLockSwitch)
        self.state.temperature = (
            resp_model.temperature / 10
        )  # Fahrenheit but without decimals

        drying_mode = resp_model.dryingMode
        if drying_mode is not None:
            self.state.drying_mode_status = DryingModes.from_int(drying_mode.dryingState)
            self.state.drying_mode_auto_switch = DeviceStatus.from_int(
                drying_mode.autoDryingSwitch
            )
            self.state.drying_mode_level = drying_mode.dryingLevel
            self.state.drying_mode_time_remain = drying_mode.dryingRemain
        if resp_model.timerRemain > 0:
            self.state.timer = Timer(
                resp_model.timerRemain,
                DeviceStatus.from_bool(self.state.device_status != DeviceStatus.ON),
            )

    async def get_details(self) -> None:
        r_dict = await self.call_bypassv2_api('getHumidifierStatus')
        r_model = process_bypassv2_result(
            self, logger, 'get_details', r_dict, models.Superior6000SResult
        )
        if r_model is None:
            return

        self._set_state(r_model)

    async def toggle_switch(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.device_status != DeviceStatus.ON

        payload_data = {'powerSwitch': int(toggle), 'switchIdx': 0}
        r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
        if r is None:
            return False

        self.state.device_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.automatic_stop_config is not True

        payload_data = {'autoStopSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setAutoStopSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_automatic_stop', self, r_dict)
        if r is None:
            return False

        self.state.automatic_stop_config = toggle
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_drying_mode(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.drying_mode_status != DeviceStatus.ON

        payload_data = {'autoDryingSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setDryingMode', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_drying_mode', self, r_dict)
        if r is None:
            return False

        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.drying_mode_auto_switch = DeviceStatus.from_bool(toggle)
        return True

    async def toggle_display(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.display_set_status != DeviceStatus.ON

        payload_data = {'screenSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
        r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
        if r is None:
            return False

        self.state.display_set_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_humidity(self, humidity: int) -> bool:
        if not Validators.validate_range(humidity, *self.target_minmax):
            logger.warning('Humidity value must be set between 30 and 80')
            return False

        payload_data = {'targetHumidity': humidity}
        r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
        r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
        if r is None:
            return False

        self.state.auto_target_humidity = humidity
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mode(self, mode: str) -> bool:
        if mode not in self.mist_modes:
            logger.warning('Invalid humidity mode used - %s', mode)
            logger.info(
                'Proper modes for this device are - %s',
                orjson.dumps(
                    self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
                ),
            )
            return False

        payload_data = {'workMode': self.mist_modes[mode]}
        r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)

        r = Helpers.process_dev_response(logger, 'set_humidity_mode', self, r_dict)
        if r is None:
            return False

        self.state.mode = mode
        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mist_level(self, level: int) -> bool:
        if level not in self.mist_levels:
            logger.warning('Humidifier mist level must be between 0 and 9')
            return False

        payload_data = {'levelIdx': 0, 'virtualLevel': level, 'levelType': 'mist'}
        r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
        if r is None:
            return False

        self.state.mist_level = level
        self.state.mist_virtual_level = level
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_child_lock(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.child_lock is True

        payload_data = {'childLockSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setChildLock', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_child_lock', self, r_dict)
        if r is None:
            return False

        self.state.child_lock = toggle
        self.state.connection_status = ConnectionStatus.ONLINE
        return True


class VeSyncHumid1000S(VeSyncHumid200300S):
    """Levoit OasisMist 1000S Specific class.

    Args:
        details (ResponseDeviceDetailsModel): The device details.
        manager (VeSync): The manager object for API calls.
        feature_map (HumidifierMap): The feature map for the device.

    Attributes:
        state (HumidifierState): The state of the humidifier.
        last_response (ResponseInfo): Last response from API call.
        manager (VeSync): Manager object for API calls.
        device_name (str): Name of device.
        device_image (str): URL for device image.
        cid (str): Device ID.
        connection_type (str): Connection type of device.
        device_type (str): Type of device.
        type (str): Type of device.
        uuid (str): UUID of device, not always present.
        config_module (str): Configuration module of device.
        mac_id (str): MAC ID of device.
        current_firm_version (str): Current firmware version of device.
        device_region (str): Region of device. (US, EU, etc.)
        pid (str): Product ID of device, pulled by some devices on update.
        sub_device_no (int): Sub-device number of device.
        product_type (str): Product type of device.
        features (dict): Features of device.
        mist_levels (list): List of mist levels.
        mist_modes (list): List of mist modes.
        target_minmax (tuple): Tuple of target min and max values.
        warm_mist_levels (list): List of warm mist levels.
    """

    __slots__ = ()

    def __init__(
        self,
        details: ResponseDeviceDetailsModel,
        manager: VeSync,
        feature_map: HumidifierMap,
    ) -> None:
        """Initialize levoit 1000S device class."""
        super().__init__(details, manager, feature_map)

    def _set_state(self, resp_model: models.InnerHumidifierBaseResult) -> None:
        """Set state of Levoit 1000S from API result model."""
        if not isinstance(resp_model, models.Levoit1000SResult):
            return
        self.state.device_status = DeviceStatus.from_int(resp_model.powerSwitch)
        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.mode = self._reverse_mist_modes.get(resp_model.workMode)
        self.state.humidity = resp_model.humidity
        self.state.auto_target_humidity = resp_model.targetHumidity
        self.state.mist_level = resp_model.mistLevel
        self.state.mist_virtual_level = resp_model.virtualLevel
        self.state.water_lacks = bool(resp_model.waterLacksState)
        self.state.water_tank_lifted = bool(resp_model.waterTankLifted)
        self.state.automatic_stop_config = bool(resp_model.autoStopSwitch)
        self.state.auto_stop_target_reached = bool(resp_model.autoStopState)
        self.state.display_set_status = DeviceStatus.from_int(resp_model.screenSwitch)
        self.state.display_status = DeviceStatus.from_int(resp_model.screenState)
        if resp_model.nightLight is not None:
            self.state.nightlight_brightness = resp_model.nightLight.brightness
            self.state.nightlight_status = DeviceStatus.from_int(
                resp_model.nightLight.nightLightSwitch
            )

    async def get_details(self) -> None:
        r_dict = await self.call_bypassv2_api('getHumidifierStatus')
        r_model = process_bypassv2_result(
            self, logger, 'get_details', r_dict, models.Levoit1000SResult
        )
        if r_model is None:
            return

        self._set_state(r_model)

    @deprecated('Use toggle_display() instead.')
    async def set_display(self, toggle: bool) -> bool:
        """Toggle display on/off.

        This is a deprecated method, please use toggle_display() instead.
        """
        return await self.toggle_switch(toggle)

    async def toggle_display(self, toggle: bool) -> bool:
        payload_data = {'screenSwitch': int(toggle)}
        body = self._build_request('setDisplay', payload_data)
        headers = Helpers.req_header_bypass()

        r_dict, _ = await self.manager.async_call_api(
            '/cloud/v2/deviceManaged/bypassV2',
            method='post',
            headers=headers,
            json_object=body.to_dict(),
        )
        r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
        if r is None:
            return False
        self.state.display_set_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mode(self, mode: str) -> bool:
        if mode not in self.mist_modes:
            logger.warning('Invalid humidity mode used - %s', mode)
            logger.info(
                'Proper modes for this device are - %s',
                orjson.dumps(
                    self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
                ),
            )
            return False

        payload_data = {'workMode': self.mist_modes[mode]}
        r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mode', self, r_dict)
        if r is None:
            return False

        self.state.mode = mode

        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mist_level(self, level: int) -> bool:
        if level not in self.mist_levels:
            logger.warning('Humidifier mist level out of range')
            return False

        payload_data = {'levelIdx': 0, 'virtualLevel': level, 'levelType': 'mist'}
        r_dict = await self.call_bypassv2_api('virtualLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
        if r is None:
            return False

        self.state.mist_level = level
        self.state.mist_virtual_level = level
        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_switch(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.device_status != DeviceStatus.ON

        payload_data = {'powerSwitch': int(toggle), 'switchIdx': 0}
        r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
        if r is None:
            return False

        self.state.device_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_humidity(self, humidity: int) -> bool:
        if not Validators.validate_range(humidity, *self.target_minmax):
            logger.warning(
                'Humidity value must be set between %s and %s',
                self.target_minmax[0],
                self.target_minmax[1],
            )
            return False

        payload_data = {'targetHumidity': humidity}
        r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
        r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
        if r is None:
            return False

        self.state.auto_target_humidity = humidity
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.automatic_stop_config is not True

        payload_data = {'autoStopSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setAutoStopSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'set_automatic_stop', self, r_dict)

        if r is None:
            return False
        self.state.automatic_stop_config = toggle
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_nightlight(self, toggle: bool | None = None) -> bool:
        if not self.supports_nightlight:
            logger.warning(
                '%s is a %s does not have a nightlight or it is not supported.',
                self.device_name,
                self.device_type,
            )
            return False

        if toggle is None:
            toggle = self.state.nightlight_status != DeviceStatus.ON

        payload_data = {'nightLightSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setNightLightStatus', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_nightlight', self, r_dict)
        if r is None:
            return False

        self.state.nightlight_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_nightlight_brightness(self, brightness: int) -> bool:
        if not self.supports_nightlight:
            logger.warning(
                '%s is a %s does not have a nightlight or it is not supported.',
                self.device_name,
                self.device_type,
            )
            return False

        if not Validators.validate_zero_to_hundred(brightness):
            logger.warning('Brightness value must be set between 0 and 100')
            return False

        payload_data = {
            'brightness': brightness,
            'nightLightSwitch': 1 if brightness > 0 else 0,
        }
        r_dict = await self.call_bypassv2_api('setLightStatus', payload_data)
        r = Helpers.process_dev_response(
            logger, 'set_night_light_brightness', self, r_dict
        )
        if r is None:
            return False

        self.state.nightlight_brightness = brightness
        self.state.nightlight_status = (
            DeviceStatus.ON if brightness > 0 else DeviceStatus.OFF
        )
        self.state.connection_status = ConnectionStatus.ONLINE
        return True


class VeSyncSproutHumid(BypassV2Mixin, VeSyncHumidifier):
    """VeSync Sprout Humidifier class.

    Args:
        details (ResponseDeviceDetailsModel): The device details.
        manager (VeSync): The manager object for API calls.
        feature_map (HumidifierMap): The feature map for the device.

    Attributes:
        state (HumidifierState): The state of the humidifier.
        last_response (ResponseInfo): Last response from API call.
        manager (VeSync): Manager object for API calls.
        device_name (str): Name of device.
        device_image (str): URL for device image.
        cid (str): Device ID.
        connection_type (str): Connection type of device.
        device_type (str): Type of device.
        type (str): Type of device.
        uuid (str): UUID of device, not always present.
        config_module (str): Configuration module of device.
        mac_id (str): MAC ID of device.
        current_firm_version (str): Current firmware version of device.
        device_region (str): Region of device. (US, EU, etc.)
        pid (str): Product ID of device, pulled by some devices on update.
        sub_device_no (int): Sub-device number of device.
        product_type (str): Product type of device.
        features (dict): Features of device.
    """

    __slots__ = ()

    def __init__(
        self,
        details: ResponseDeviceDetailsModel,
        manager: VeSync,
        feature_map: HumidifierMap,
    ) -> None:
        """Initialize Sprout Humidifier class."""
        super().__init__(details, manager, feature_map)

    def _set_state(self, resp_model: models.SproutHumidifierResult) -> None:
        """Set state from Sprout Humidifier API result model."""
        self.state.device_status = DeviceStatus.from_int(resp_model.powerSwitch)
        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.humidity = resp_model.humidity
        self.state.auto_target_humidity = resp_model.targetHumidity
        self.state.mist_virtual_level = resp_model.virtualLevel
        self.state.mist_level = resp_model.mistLevel
        self.state.mode = self._reverse_mist_modes.get(resp_model.workMode)
        self.state.water_lacks = bool(resp_model.waterLacksState)
        self.state.water_tank_lifted = bool(resp_model.waterTankLifted)
        self.state.automatic_stop_config = bool(resp_model.autoStopSwitch)
        self.state.auto_stop_target_reached = bool(resp_model.autoStopState)
        self.state.display_set_status = DeviceStatus.from_int(resp_model.screenSwitch)
        self.state.display_status = DeviceStatus.from_int(resp_model.screenState)
        self.state.auto_preference = resp_model.autoPreference
        self.state.water_lacks_drying_switch = DeviceStatus.from_int(
            resp_model.waterShortageDryingSwitch
        )
        self.state.child_lock = bool(resp_model.childLockSwitch)
        self.state.filter_life = resp_model.filterLifePercent
        self.state.hepa_filter_life = resp_model.hepaFilterLifePercent
        self.state.temperature = (
            resp_model.temperature / 10
        )  # Response needs to be divided by 10
        if resp_model.breathingLamp is not None:
            self.state.breathing_lamp = BreathingLampState.from_model(
                resp_model.breathingLamp
            )
        if resp_model.nightLight is not None:
            self.state.nightlight_brightness = resp_model.nightLight.brightness
            self.state.nightlight_status = DeviceStatus.from_int(
                resp_model.nightLight.nightLightSwitch
            )
            self.state.nightlight_color_temp = resp_model.nightLight.colorTemperature
        if resp_model.dryingMode is not None:
            drying_mode = resp_model.dryingMode
            self.state.drying_mode_status = DryingModes.from_int(drying_mode.dryingState)
            self.state.drying_mode_auto_switch = DeviceStatus.from_int(
                drying_mode.autoDryingSwitch
            )
            self.state.drying_mode_time_remain = drying_mode.dryingRemain
            self.state.drying_mode_level = drying_mode.dryingLevel

    async def get_details(self) -> None:
        r_dict = await self.call_bypassv2_api('getHumidifierStatus')
        r_model = process_bypassv2_result(
            self, logger, 'get_details', r_dict, models.SproutHumidifierResult
        )
        if r_model is None:
            return
        self._set_state(r_model)

    async def toggle_switch(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.device_status != DeviceStatus.ON

        payload_data = {'powerSwitch': int(toggle), 'switchIdx': 0}
        r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
        if r is None:
            return False

        self.state.device_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mode(self, mode: str) -> bool:
        if mode not in self.mist_modes:
            logger.warning('Invalid humidity mode used - %s', mode)
            logger.info(
                'Proper modes for this device are - %s',
                orjson.dumps(
                    self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
                ),
            )
            return False
        payload_data = {'workMode': self.mist_modes[mode]}
        r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mode', self, r_dict)
        if r is None:
            return False
        self.state.mode = mode
        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_display(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.display_set_status != DeviceStatus.ON

        payload_data = {'screenSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
        r = Helpers.process_dev_response(logger, 'set_display', self, r_dict)
        if r is None:
            return False

        self.state.display_set_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.display_status = DeviceStatus.from_bool(toggle)
        return True

    async def set_mist_level(self, level: int) -> bool:
        if level not in self.mist_levels:
            logger.warning('Humidifier mist level out of range')
            return False

        payload_data = {'levelIdx': 0, 'virtualLevel': level, 'levelType': 'mist'}
        r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
        if r is None:
            return False

        self.state.mist_level = level
        self.state.mist_virtual_level = level
        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_humidity(self, humidity: int) -> bool:
        if not Validators.validate_range(humidity, *self.target_minmax):
            logger.warning(
                'Humidity value must be set between %s and %s',
                self.target_minmax[0],
                self.target_minmax[1],
            )
            return False

        payload_data = {'targetHumidity': humidity}
        r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
        r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
        if r is None:
            return False

        self.state.auto_target_humidity = humidity
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_drying_mode(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.drying_mode_status != DeviceStatus.ON

        payload_data = {'autoDryingSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setDryingMode', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_drying_mode', self, r_dict)
        if r is None:
            return False

        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.drying_mode_auto_switch = DeviceStatus.from_bool(toggle)
        return True

    async def toggle_child_lock(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.child_lock is True

        payload_data = {'childLockSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setChildLock', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_child_lock', self, r_dict)
        if r is None:
            return False

        self.state.child_lock = toggle
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def _set_nightlight_state(
        self,
        toggle: bool,
        brightness: int | None = None,
        color_temp: int | None = None,
    ) -> bool:
        if brightness is not None and not Validators.validate_zero_to_hundred(brightness):
            logger.warning('Brightness value must be set between 0 and 100')
            return False

        if color_temp is not None and not Validators.validate_range(
            color_temp, 2000, 3500
        ):
            logger.warning('Color temperature must be set between 2000K and 3500K')
            return False

        if self.state.nightlight_color_temp is None:
            self.state.nightlight_color_temp = 3500  # Default color temp if not set

        payload_data = {
            'brightness': brightness or self.state.nightlight_brightness,
            'colorTemperature': color_temp or self.state.nightlight_color_temp,
            'nightLightSwitch': int(toggle),
        }
        r_dict = await self.call_bypassv2_api('setLightStatus', payload_data)
        r = Helpers.process_dev_response(logger, 'set_night_light_state', self, r_dict)
        if r is None:
            return False

        self.state.nightlight_brightness = brightness
        self.state.nightlight_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_automatic_stop(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.automatic_stop_config is not True

        payload_data = {'autoStopSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setAutoStopSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_automatic_stop', self, r_dict)
        if r is None:
            return False

        self.state.automatic_stop_config = toggle
        self.state.connection_status = ConnectionStatus.ONLINE
        return True


class VeSyncLV600S(BypassV2Mixin, VeSyncHumidifier):
    """LV600S (LUH-A603S) Humidifier with warm mist support.

    This class handles the newer LV600S models that use the new API format
    with powerSwitch, workMode, etc. and include warm mist functionality.

    Args:
        details (ResponseDeviceDetailsModel): The device details.
        manager (VeSync): The manager object for API calls.
        feature_map (HumidifierMap): The feature map for the device.

    Attributes:
        state (HumidifierState): The state of the humidifier.
        last_response (ResponseInfo): Last response from API call.
        manager (VeSync): Manager object for API calls.
        device_name (str): Name of device.
        device_image (str): URL for device image.
        cid (str): Device ID.
        connection_type (str): Connection type of device.
        device_type (str): Type of device.
        type (str): Type of device.
        uuid (str): UUID of device, not always present.
        config_module (str): Configuration module of device.
        mac_id (str): MAC ID of device.
        current_firm_version (str): Current firmware version of device.
        device_region (str): Region of device. (US, EU, etc.)
        pid (str): Product ID of device, pulled by some devices on update.
        sub_device_no (int): Sub-device number of device.
        product_type (str): Product type of device.
        features (dict): Features of device.
        mist_levels (list): List of mist levels.
        mist_modes (list): List of mist modes.
        target_minmax (tuple): Tuple of target min and max values.
        warm_mist_levels (list): List of warm mist levels.
    """

    __slots__ = ()

    def __init__(
        self,
        details: ResponseDeviceDetailsModel,
        manager: VeSync,
        feature_map: HumidifierMap,
    ) -> None:
        """Initialize LV600S Humidifier class."""
        super().__init__(details, manager, feature_map)

    def _set_state(self, resp_model: models.LV600SResult) -> None:
        """Set state from LV600S API result model."""
        self.state.device_status = DeviceStatus.from_int(resp_model.powerSwitch)
        self.state.connection_status = ConnectionStatus.ONLINE
        self.state.mode = self._reverse_mist_modes.get(resp_model.workMode)
        if self.state.mode is None:
            logger.warning('Unknown mist mode received: %s', resp_model.workMode)

        self.state.auto_target_humidity = resp_model.targetHumidity
        self.state.humidity = resp_model.humidity
        self.state.mist_level = resp_model.mistLevel
        self.state.mist_virtual_level = resp_model.virtualLevel
        self.state.water_lacks = bool(resp_model.waterLacksState)
        self.state.water_tank_lifted = bool(resp_model.waterTankLifted)
        self.state.automatic_stop_config = bool(resp_model.autoStopSwitch)
        self.state.auto_stop_target_reached = bool(resp_model.autoStopState)
        self.state.display_set_status = DeviceStatus.from_int(resp_model.screenSwitch)
        self.state.display_status = DeviceStatus.from_int(resp_model.screenState)
        # Warm mist support
        self.state.warm_mist_enabled = resp_model.warmPower
        self.state.warm_mist_level = resp_model.warmLevel

        if resp_model.timerRemain > 0:
            self.state.timer = Timer(
                resp_model.timerRemain,
                DeviceStatus.from_bool(self.state.device_status != DeviceStatus.ON),
            )

    async def get_details(self) -> None:
        r_dict = await self.call_bypassv2_api('getHumidifierStatus')
        r_model = process_bypassv2_result(
            self, logger, 'get_details', r_dict, models.LV600SResult
        )
        if r_model is None:
            return

        self._set_state(r_model)

    async def toggle_switch(self, toggle: bool | None = None) -> bool:
        if toggle is None:
            toggle = self.state.device_status != DeviceStatus.ON

        payload_data = {'powerSwitch': int(toggle), 'switchIdx': 0}
        r_dict = await self.call_bypassv2_api('setSwitch', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_switch', self, r_dict)
        if r is None:
            return False

        self.state.device_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_humidity(self, humidity: int) -> bool:
        if not Validators.validate_range(humidity, *self.target_minmax):
            logger.warning(
                'Humidity value must be set between %s and %s',
                self.target_minmax[0],
                self.target_minmax[1],
            )
            return False

        payload_data = {'targetHumidity': humidity}
        r_dict = await self.call_bypassv2_api('setTargetHumidity', payload_data)
        r = Helpers.process_dev_response(logger, 'set_humidity', self, r_dict)
        if r is None:
            return False

        self.state.auto_target_humidity = humidity
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mode(self, mode: str) -> bool:
        if mode not in self.mist_modes:
            logger.warning('Invalid humidity mode used - %s', mode)
            logger.info(
                'Proper modes for this device are - %s',
                orjson.dumps(
                    self.mist_modes, option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
                ),
            )
            return False

        payload_data = {'workMode': self.mist_modes[mode]}
        r_dict = await self.call_bypassv2_api('setHumidityMode', payload_data)

        r = Helpers.process_dev_response(logger, 'set_mode', self, r_dict)
        if r is None:
            return False

        self.state.mode = mode
        self.state.device_status = DeviceStatus.ON
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_mist_level(self, level: int) -> bool:
        if level not in self.mist_levels:
            logger.warning('Humidifier mist level must be between 1 and 9')
            return False

        payload_data = {'levelIdx': 0, 'virtualLevel': level, 'levelType': 'mist'}
        r_dict = await self.call_bypassv2_api('setVirtualLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_mist_level', self, r_dict)
        if r is None:
            return False

        self.state.mist_level = level
        self.state.mist_virtual_level = level
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def set_warm_level(self, warm_level: int) -> bool:
        """Set warm mist level (0 = off, 1-3 = warm levels)."""
        if not self.supports_warm_mist:
            logger.debug(
                '%s is a %s does not have a mist warmer',
                self.device_name,
                self.device_type,
            )
            return False

        if warm_level not in self.warm_mist_levels:
            logger.warning('Warm mist level must be one of %s', self.warm_mist_levels)
            return False

        payload_data = {
            'levelIdx': 0,
            'levelType': 'warm',
            'mistLevel': 0,
            'warmLevel': warm_level,
        }
        r_dict = await self.call_bypassv2_api('setLevel', payload_data)
        r = Helpers.process_dev_response(logger, 'set_warm_level', self, r_dict)
        if r is None:
            return False

        self.state.warm_mist_level = warm_level
        self.state.warm_mist_enabled = warm_level > 0
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def toggle_display(self, toggle: bool | None = None) -> bool:
        """Toggle display on/off."""
        if toggle is None:
            toggle = self.state.display_set_status != DeviceStatus.ON

        payload_data = {'screenSwitch': int(toggle)}
        r_dict = await self.call_bypassv2_api('setDisplay', payload_data)
        r = Helpers.process_dev_response(logger, 'toggle_display', self, r_dict)
        if r is None:
            return False

        self.state.display_set_status = DeviceStatus.from_bool(toggle)
        self.state.display_status = DeviceStatus.from_bool(toggle)
        self.state.connection_status = ConnectionStatus.ONLINE
        return True

    async def get_timer(self) -> Timer | None:
        """Get timer for humidifier using V2 API."""
        r_dict = await self.call_bypassv2_api('getTimerV2', {})
        result_model = process_bypassv2_result(
            self, logger, 'get_timer', r_dict, ResultV2GetTimerV2
        )
        if result_model is None:
            return None
        if not result_model.timers:
            logger.debug('No timers found')
            return None
        timer = result_model.timers[0]
        action = DeviceStatus.OFF
        if timer.startAct:
            action = DeviceStatus.ON if timer.startAct[0].act == 1 else DeviceStatus.OFF
        self.state.timer = Timer(
            timer_duration=timer.total,
            action=action,
            id=timer.id,
            remaining=timer.remain,
        )
        return self.state.timer

    async def clear_timer(self) -> bool:
        """Clear timer for humidifier using V2 API."""
        timer_id = self.state.timer.id if self.state.timer else 1
        payload = {'id': timer_id}
        r_dict = await self.call_bypassv2_api('delTimerV2', payload)
        r = Helpers.process_dev_response(logger, 'clear_timer', self, r_dict)
        if r is None:
            return False
        self.state.timer = None
        return True

    async def set_timer(self, duration: int, action: str | None = None) -> bool:
        """Set timer for humidifier using V2 API.

        Args:
            duration: Timer duration in seconds.
            action: Action when timer expires. Defaults to toggle current state.
        """
        if action is None:
            action = (
                DeviceStatus.OFF
                if self.state.device_status == DeviceStatus.ON
                else DeviceStatus.ON
            )
        act_val = 1 if action == DeviceStatus.ON else 0

        payload_data = {
            'startAct': [{'type': 'powerSwitch', 'num': 0, 'act': act_val}],
            'total': duration,
        }
        r_dict = await self.call_bypassv2_api('addTimerV2', payload_data)
        result_model = process_bypassv2_result(
            self, logger, 'set_timer', r_dict, ResultV2SetTimer
        )
        if result_model is None:
            return False

        self.state.timer = Timer(
            timer_duration=duration,
            action=action,
            id=result_model.id,
            remaining=duration,
        )
        return True
