File: bot.py

package info (click to toggle)
pyswitchbot 1.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 980 kB
  • sloc: python: 14,812; makefile: 2
file content (123 lines) | stat: -rw-r--r-- 4,121 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Library to handle connection with Switchbot."""

from __future__ import annotations

import logging
from typing import Any

from .device import (
    DEVICE_SET_EXTENDED_KEY,
    DEVICE_SET_MODE_KEY,
    SwitchbotDeviceOverrideStateDuringConnection,
    update_after_operation,
)

_LOGGER = logging.getLogger(__name__)

BOT_COMMAND_HEADER = "5701"

# Bot keys
PRESS_KEY = f"{BOT_COMMAND_HEADER}00"
ON_KEY = f"{BOT_COMMAND_HEADER}01"
OFF_KEY = f"{BOT_COMMAND_HEADER}02"
DOWN_KEY = f"{BOT_COMMAND_HEADER}03"
UP_KEY = f"{BOT_COMMAND_HEADER}04"


class Switchbot(SwitchbotDeviceOverrideStateDuringConnection):
    """Representation of a Switchbot."""

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Switchbot Bot/WoHand constructor."""
        super().__init__(*args, **kwargs)
        self._inverse: bool = kwargs.pop("inverse_mode", False)

    @update_after_operation
    async def turn_on(self) -> bool:
        """Turn device on."""
        result = await self._send_command(ON_KEY)
        ret = self._check_command_result(result, 0, {1, 5})
        self._override_state({"isOn": True})
        _LOGGER.debug(
            "%s: Turn on result: %s -> %s",
            self.name,
            result.hex() if result else None,
            self._override_adv_data,
        )
        self._fire_callbacks()
        return ret

    @update_after_operation
    async def turn_off(self) -> bool:
        """Turn device off."""
        result = await self._send_command(OFF_KEY)
        ret = self._check_command_result(result, 0, {1, 5})
        self._override_state({"isOn": False})
        _LOGGER.debug(
            "%s: Turn off result: %s -> %s",
            self.name,
            result.hex() if result else None,
            self._override_adv_data,
        )
        self._fire_callbacks()
        return ret

    @update_after_operation
    async def hand_up(self) -> bool:
        """Raise device arm."""
        result = await self._send_command(UP_KEY)
        return self._check_command_result(result, 0, {1, 5})

    @update_after_operation
    async def hand_down(self) -> bool:
        """Lower device arm."""
        result = await self._send_command(DOWN_KEY)
        return self._check_command_result(result, 0, {1, 5})

    @update_after_operation
    async def press(self) -> bool:
        """Press command to device."""
        result = await self._send_command(PRESS_KEY)
        return self._check_command_result(result, 0, {1, 5})

    @update_after_operation
    async def set_switch_mode(
        self, switch_mode: bool = False, strength: int = 100, inverse: bool = False
    ) -> bool:
        """Change bot mode."""
        mode_key = format(switch_mode, "b") + format(inverse, "b")
        strength_key = f"{strength:0{2}x}"  # to hex with padding to double digit
        result = await self._send_command(DEVICE_SET_MODE_KEY + strength_key + mode_key)
        return self._check_command_result(result, 0, {1})

    @update_after_operation
    async def set_long_press(self, duration: int = 0) -> bool:
        """Set bot long press duration."""
        duration_key = f"{duration:0{2}x}"  # to hex with padding to double digit
        result = await self._send_command(DEVICE_SET_EXTENDED_KEY + "08" + duration_key)
        return self._check_command_result(result, 0, {1})

    async def get_basic_info(self) -> dict[str, Any] | None:
        """Get device basic settings."""
        if not (_data := await self._get_basic_info()):
            return None
        return {
            "battery": _data[1],
            "firmware": _data[2] / 10.0,
            "strength": _data[3],
            "timers": _data[8],
            "switchMode": bool(_data[9] & 16),
            "inverseDirection": bool(_data[9] & 1),
            "holdSeconds": _data[10],
        }

    def is_on(self) -> bool | None:
        """Return switch state from cache."""
        # To get actual position call update() first.
        value = self._get_adv_value("isOn")
        if value is None:
            return None

        if self._inverse:
            return not value
        return value