Controllers.smart_plug

Контроллер умной розетки Яндекс Умного дома.

Позволяет получать состояние розетки (напряжение, ток, потребление) и управлять ей (включить/выключить) через Yandex IoT API.

  1"""
  2Контроллер умной розетки Яндекс Умного дома.
  3
  4Позволяет получать состояние розетки (напряжение, ток, потребление)
  5и управлять ей (включить/выключить) через Yandex IoT API.
  6"""
  7
  8import time
  9import requests
 10
 11from langfuse.decorators import observe
 12from loguru import logger
 13
 14from Controllers.controller import Controller
 15from DataSources import YandexDataSource
 16
 17
 18class SmartPlugController(Controller, YandexDataSource):
 19    """Контроллер для управления умной розеткой Яндекс Умного дома.
 20
 21    Поддерживает получение состояния (напряжение, сила тока,
 22    потребляемая мощность) и включение/выключение розетки.
 23
 24    Attributes:
 25        device_id: Идентификатор устройства в Яндекс Умном доме.
 26        round_digits: Кортеж из трёх значений для округления (вольты, амперы, ватты).
 27        state_retry_count: Количество попыток получения состояния.
 28        state_retry_delay: Задержка между попытками в секундах.
 29        state: Последнее полученное состояние розетки.
 30    """
 31
 32    _default_round_digits = (0, 2, 0)
 33
 34    def __init__(
 35        self, device_id, api_key, round_digits=None, state_retry_count=3, state_retry_delay=2
 36    ):
 37        """Инициализирует контроллер розетки.
 38
 39        Args:
 40            device_id: Идентификатор устройства в Яндекс Умном доме.
 41            api_key: Токен авторизации Yandex IoT API.
 42            round_digits: Кортеж с количеством знаков после запятой.
 43            state_retry_count: Количество попыток получения состояния.
 44            state_retry_delay: Задержка между попытками в секундах.
 45        """
 46
 47        self.device_id = device_id
 48
 49        Controller.__init__(self)
 50        YandexDataSource.__init__(self, api_key)
 51
 52        self.round_digits = round_digits or self._default_round_digits
 53        self.state_retry_count = state_retry_count
 54        self.state_retry_delay = state_retry_delay
 55        self.state = self.get_state()
 56
 57    def __str__(self):
 58        """Возвращает строковое представление состояния розетки.
 59
 60        Returns:
 61            str: Строка вида "розетка: включена, напряжение: 220 вольт, ...".
 62        """
 63        # используем .get() и разные кавычки, чтобы избежать синтаксических ошибок
 64        power = self.state.get("power", False)
 65        vr, ar, cr = self.round_digits
 66        voltage = round(self.state.get("voltage", 0), vr)
 67        voltage = int(voltage) if vr == 0 else voltage
 68        amperage = round(self.state.get("amperage", 0), ar)
 69        amperage = int(amperage) if ar == 0 else amperage
 70        consumption = round(self.state.get("power_consumption", 0), cr)
 71        consumption = int(consumption) if cr == 0 else consumption
 72        return f"розетка: {'включена' if power else 'выключена'}, напряжение: {voltage} вольт, сила тока: {amperage} ампер, потребление: {consumption} ватт"
 73
 74    @observe(name="Функция для получения состояния розетки")
 75    def get_state(self):
 76        """Получает состояние розетки через Yandex IoT API.
 77
 78        Выполняет GET-запрос с повторными попытками. Разбирает
 79        capabilities и properties устройства: напряжение, ток,
 80        потребляемую мощность.
 81
 82        Returns:
 83            dict: Словарь с ключами device_id, name, online, power,
 84                  voltage, amperage, power_consumption. При ошибках
 85                  возвращает значения по умолчанию.
 86        """
 87
 88        for attempt in range(self.state_retry_count):
 89            try:
 90                response = requests.get(
 91                    self.url + f"/{self.device_id}", headers=self.yandex_headers, timeout=10
 92                )
 93                response.raise_for_status()
 94
 95                device = response.json()
 96
 97                state = {
 98                    "device_id": self.device_id,
 99                    "name": device.get("name", ""),
100                    "online": device.get("state", "unknown"),
101                }
102
103                # Обрабатываем capabilities и properties как в первой функции
104                for capability in device.get("capabilities", []):
105                    if capability.get("type") == "devices.capabilities.on_off":
106                        state["power"] = capability.get("state", {}).get("value", False)
107
108                for property in device.get("properties", []):
109                    prop_type = property.get("type")
110                    value = property.get("state", {}).get("value")
111
112                    if prop_type == "devices.properties.float":
113                        parameters = property.get("parameters", {})
114                        instance = parameters.get("instance", "")
115
116                        if "voltage" in instance:
117                            state["voltage"] = value
118                        elif "amperage" in instance:
119                            state["amperage"] = value
120                        elif "power" in instance:
121                            state["power_consumption"] = value
122
123                self.state = state
124                return state
125            except requests.exceptions.RequestException as e:
126                logger.error(f"Попытка {attempt + 1}/{self.state_retry_count}: {e}")
127                if attempt < self.state_retry_count - 1:
128                    time.sleep(self.state_retry_delay)
129
130        self.state = {
131            "device_id": self.device_id,
132            "name": "",
133            "online": "unknown",
134            "power": False,
135            "voltage": 0,
136            "amperage": 0,
137            "power_consumption": 0,
138        }
139        logger.error(
140            f"Не удалось получить состояние розетки после {self.state_retry_count} попыток"
141        )
142        return self.state
143
144    @observe(name="Управление розеткой (включение/выключение)")
145    def control_plug(self, turn_on):
146        """Включает или выключает розетку через Yandex IoT API.
147
148        Отправляет POST-запрос с действием on_off на устройство.
149
150        Args:
151            turn_on: True для включения, False для выключения.
152
153        Returns:
154            bool: True если команда выполнена успешно, False при ошибке.
155        """
156        try:
157            payload = {
158                "devices": [
159                    {
160                        "id": self.device_id,
161                        "actions": [
162                            {
163                                "type": "devices.capabilities.on_off",
164                                "state": {"instance": "on", "value": turn_on},
165                            }
166                        ],
167                    }
168                ]
169            }
170
171            response = requests.post(
172                self.url + "/actions", headers=self.yandex_headers, json=payload, timeout=10
173            )
174
175            response.raise_for_status()
176
177            action_text = "включена" if turn_on else "выключена"
178            logger.debug(f"Розетка успешно {action_text}")
179
180            return True
181
182        except requests.exceptions.RequestException as e:
183            logger.error(f"Ошибка при управлении розеткой: {e}")
184            if hasattr(e, "response") and e.response is not None:
185                logger.error(f"Статус код: {e.response.status_code}")
186                logger.error(f"Ответ: {e.response.text}")
187            return False
188        except Exception as e:
189            logger.error(f"Неожиданная ошибка при управлении розеткой: {e}")
190            return False
 19class SmartPlugController(Controller, YandexDataSource):
 20    """Контроллер для управления умной розеткой Яндекс Умного дома.
 21
 22    Поддерживает получение состояния (напряжение, сила тока,
 23    потребляемая мощность) и включение/выключение розетки.
 24
 25    Attributes:
 26        device_id: Идентификатор устройства в Яндекс Умном доме.
 27        round_digits: Кортеж из трёх значений для округления (вольты, амперы, ватты).
 28        state_retry_count: Количество попыток получения состояния.
 29        state_retry_delay: Задержка между попытками в секундах.
 30        state: Последнее полученное состояние розетки.
 31    """
 32
 33    _default_round_digits = (0, 2, 0)
 34
 35    def __init__(
 36        self, device_id, api_key, round_digits=None, state_retry_count=3, state_retry_delay=2
 37    ):
 38        """Инициализирует контроллер розетки.
 39
 40        Args:
 41            device_id: Идентификатор устройства в Яндекс Умном доме.
 42            api_key: Токен авторизации Yandex IoT API.
 43            round_digits: Кортеж с количеством знаков после запятой.
 44            state_retry_count: Количество попыток получения состояния.
 45            state_retry_delay: Задержка между попытками в секундах.
 46        """
 47
 48        self.device_id = device_id
 49
 50        Controller.__init__(self)
 51        YandexDataSource.__init__(self, api_key)
 52
 53        self.round_digits = round_digits or self._default_round_digits
 54        self.state_retry_count = state_retry_count
 55        self.state_retry_delay = state_retry_delay
 56        self.state = self.get_state()
 57
 58    def __str__(self):
 59        """Возвращает строковое представление состояния розетки.
 60
 61        Returns:
 62            str: Строка вида "розетка: включена, напряжение: 220 вольт, ...".
 63        """
 64        # используем .get() и разные кавычки, чтобы избежать синтаксических ошибок
 65        power = self.state.get("power", False)
 66        vr, ar, cr = self.round_digits
 67        voltage = round(self.state.get("voltage", 0), vr)
 68        voltage = int(voltage) if vr == 0 else voltage
 69        amperage = round(self.state.get("amperage", 0), ar)
 70        amperage = int(amperage) if ar == 0 else amperage
 71        consumption = round(self.state.get("power_consumption", 0), cr)
 72        consumption = int(consumption) if cr == 0 else consumption
 73        return f"розетка: {'включена' if power else 'выключена'}, напряжение: {voltage} вольт, сила тока: {amperage} ампер, потребление: {consumption} ватт"
 74
 75    @observe(name="Функция для получения состояния розетки")
 76    def get_state(self):
 77        """Получает состояние розетки через Yandex IoT API.
 78
 79        Выполняет GET-запрос с повторными попытками. Разбирает
 80        capabilities и properties устройства: напряжение, ток,
 81        потребляемую мощность.
 82
 83        Returns:
 84            dict: Словарь с ключами device_id, name, online, power,
 85                  voltage, amperage, power_consumption. При ошибках
 86                  возвращает значения по умолчанию.
 87        """
 88
 89        for attempt in range(self.state_retry_count):
 90            try:
 91                response = requests.get(
 92                    self.url + f"/{self.device_id}", headers=self.yandex_headers, timeout=10
 93                )
 94                response.raise_for_status()
 95
 96                device = response.json()
 97
 98                state = {
 99                    "device_id": self.device_id,
100                    "name": device.get("name", ""),
101                    "online": device.get("state", "unknown"),
102                }
103
104                # Обрабатываем capabilities и properties как в первой функции
105                for capability in device.get("capabilities", []):
106                    if capability.get("type") == "devices.capabilities.on_off":
107                        state["power"] = capability.get("state", {}).get("value", False)
108
109                for property in device.get("properties", []):
110                    prop_type = property.get("type")
111                    value = property.get("state", {}).get("value")
112
113                    if prop_type == "devices.properties.float":
114                        parameters = property.get("parameters", {})
115                        instance = parameters.get("instance", "")
116
117                        if "voltage" in instance:
118                            state["voltage"] = value
119                        elif "amperage" in instance:
120                            state["amperage"] = value
121                        elif "power" in instance:
122                            state["power_consumption"] = value
123
124                self.state = state
125                return state
126            except requests.exceptions.RequestException as e:
127                logger.error(f"Попытка {attempt + 1}/{self.state_retry_count}: {e}")
128                if attempt < self.state_retry_count - 1:
129                    time.sleep(self.state_retry_delay)
130
131        self.state = {
132            "device_id": self.device_id,
133            "name": "",
134            "online": "unknown",
135            "power": False,
136            "voltage": 0,
137            "amperage": 0,
138            "power_consumption": 0,
139        }
140        logger.error(
141            f"Не удалось получить состояние розетки после {self.state_retry_count} попыток"
142        )
143        return self.state
144
145    @observe(name="Управление розеткой (включение/выключение)")
146    def control_plug(self, turn_on):
147        """Включает или выключает розетку через Yandex IoT API.
148
149        Отправляет POST-запрос с действием on_off на устройство.
150
151        Args:
152            turn_on: True для включения, False для выключения.
153
154        Returns:
155            bool: True если команда выполнена успешно, False при ошибке.
156        """
157        try:
158            payload = {
159                "devices": [
160                    {
161                        "id": self.device_id,
162                        "actions": [
163                            {
164                                "type": "devices.capabilities.on_off",
165                                "state": {"instance": "on", "value": turn_on},
166                            }
167                        ],
168                    }
169                ]
170            }
171
172            response = requests.post(
173                self.url + "/actions", headers=self.yandex_headers, json=payload, timeout=10
174            )
175
176            response.raise_for_status()
177
178            action_text = "включена" if turn_on else "выключена"
179            logger.debug(f"Розетка успешно {action_text}")
180
181            return True
182
183        except requests.exceptions.RequestException as e:
184            logger.error(f"Ошибка при управлении розеткой: {e}")
185            if hasattr(e, "response") and e.response is not None:
186                logger.error(f"Статус код: {e.response.status_code}")
187                logger.error(f"Ответ: {e.response.text}")
188            return False
189        except Exception as e:
190            logger.error(f"Неожиданная ошибка при управлении розеткой: {e}")
191            return False

Контроллер для управления умной розеткой Яндекс Умного дома.

Поддерживает получение состояния (напряжение, сила тока, потребляемая мощность) и включение/выключение розетки.

Attributes: device_id: Идентификатор устройства в Яндекс Умном доме. round_digits: Кортеж из трёх значений для округления (вольты, амперы, ватты). state_retry_count: Количество попыток получения состояния. state_retry_delay: Задержка между попытками в секундах. state: Последнее полученное состояние розетки.

SmartPlugController( device_id, api_key, round_digits=None, state_retry_count=3, state_retry_delay=2)
35    def __init__(
36        self, device_id, api_key, round_digits=None, state_retry_count=3, state_retry_delay=2
37    ):
38        """Инициализирует контроллер розетки.
39
40        Args:
41            device_id: Идентификатор устройства в Яндекс Умном доме.
42            api_key: Токен авторизации Yandex IoT API.
43            round_digits: Кортеж с количеством знаков после запятой.
44            state_retry_count: Количество попыток получения состояния.
45            state_retry_delay: Задержка между попытками в секундах.
46        """
47
48        self.device_id = device_id
49
50        Controller.__init__(self)
51        YandexDataSource.__init__(self, api_key)
52
53        self.round_digits = round_digits or self._default_round_digits
54        self.state_retry_count = state_retry_count
55        self.state_retry_delay = state_retry_delay
56        self.state = self.get_state()

Инициализирует контроллер розетки.

Args: device_id: Идентификатор устройства в Яндекс Умном доме. api_key: Токен авторизации Yandex IoT API. round_digits: Кортеж с количеством знаков после запятой. state_retry_count: Количество попыток получения состояния. state_retry_delay: Задержка между попытками в секундах.

device_id
round_digits
state_retry_count
state_retry_delay
state
@observe(name='Функция для получения состояния розетки')
def get_state(self):
 75    @observe(name="Функция для получения состояния розетки")
 76    def get_state(self):
 77        """Получает состояние розетки через Yandex IoT API.
 78
 79        Выполняет GET-запрос с повторными попытками. Разбирает
 80        capabilities и properties устройства: напряжение, ток,
 81        потребляемую мощность.
 82
 83        Returns:
 84            dict: Словарь с ключами device_id, name, online, power,
 85                  voltage, amperage, power_consumption. При ошибках
 86                  возвращает значения по умолчанию.
 87        """
 88
 89        for attempt in range(self.state_retry_count):
 90            try:
 91                response = requests.get(
 92                    self.url + f"/{self.device_id}", headers=self.yandex_headers, timeout=10
 93                )
 94                response.raise_for_status()
 95
 96                device = response.json()
 97
 98                state = {
 99                    "device_id": self.device_id,
100                    "name": device.get("name", ""),
101                    "online": device.get("state", "unknown"),
102                }
103
104                # Обрабатываем capabilities и properties как в первой функции
105                for capability in device.get("capabilities", []):
106                    if capability.get("type") == "devices.capabilities.on_off":
107                        state["power"] = capability.get("state", {}).get("value", False)
108
109                for property in device.get("properties", []):
110                    prop_type = property.get("type")
111                    value = property.get("state", {}).get("value")
112
113                    if prop_type == "devices.properties.float":
114                        parameters = property.get("parameters", {})
115                        instance = parameters.get("instance", "")
116
117                        if "voltage" in instance:
118                            state["voltage"] = value
119                        elif "amperage" in instance:
120                            state["amperage"] = value
121                        elif "power" in instance:
122                            state["power_consumption"] = value
123
124                self.state = state
125                return state
126            except requests.exceptions.RequestException as e:
127                logger.error(f"Попытка {attempt + 1}/{self.state_retry_count}: {e}")
128                if attempt < self.state_retry_count - 1:
129                    time.sleep(self.state_retry_delay)
130
131        self.state = {
132            "device_id": self.device_id,
133            "name": "",
134            "online": "unknown",
135            "power": False,
136            "voltage": 0,
137            "amperage": 0,
138            "power_consumption": 0,
139        }
140        logger.error(
141            f"Не удалось получить состояние розетки после {self.state_retry_count} попыток"
142        )
143        return self.state

Получает состояние розетки через Yandex IoT API.

Выполняет GET-запрос с повторными попытками. Разбирает capabilities и properties устройства: напряжение, ток, потребляемую мощность.

Returns: dict: Словарь с ключами device_id, name, online, power, voltage, amperage, power_consumption. При ошибках возвращает значения по умолчанию.

@observe(name='Управление розеткой (включение/выключение)')
def control_plug(self, turn_on):
145    @observe(name="Управление розеткой (включение/выключение)")
146    def control_plug(self, turn_on):
147        """Включает или выключает розетку через Yandex IoT API.
148
149        Отправляет POST-запрос с действием on_off на устройство.
150
151        Args:
152            turn_on: True для включения, False для выключения.
153
154        Returns:
155            bool: True если команда выполнена успешно, False при ошибке.
156        """
157        try:
158            payload = {
159                "devices": [
160                    {
161                        "id": self.device_id,
162                        "actions": [
163                            {
164                                "type": "devices.capabilities.on_off",
165                                "state": {"instance": "on", "value": turn_on},
166                            }
167                        ],
168                    }
169                ]
170            }
171
172            response = requests.post(
173                self.url + "/actions", headers=self.yandex_headers, json=payload, timeout=10
174            )
175
176            response.raise_for_status()
177
178            action_text = "включена" if turn_on else "выключена"
179            logger.debug(f"Розетка успешно {action_text}")
180
181            return True
182
183        except requests.exceptions.RequestException as e:
184            logger.error(f"Ошибка при управлении розеткой: {e}")
185            if hasattr(e, "response") and e.response is not None:
186                logger.error(f"Статус код: {e.response.status_code}")
187                logger.error(f"Ответ: {e.response.text}")
188            return False
189        except Exception as e:
190            logger.error(f"Неожиданная ошибка при управлении розеткой: {e}")
191            return False

Включает или выключает розетку через Yandex IoT API.

Отправляет POST-запрос с действием on_off на устройство.

Args: turn_on: True для включения, False для выключения.

Returns: bool: True если команда выполнена успешно, False при ошибке.