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: Последнее полученное состояние розетки.
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: Задержка между попытками в секундах.
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. При ошибках возвращает значения по умолчанию.
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 при ошибке.