186 lines
6.0 KiB
Python
186 lines
6.0 KiB
Python
from abc import ABC, abstractmethod
|
|
from base64 import b64encode
|
|
import binascii
|
|
import requests
|
|
import logging
|
|
import json
|
|
|
|
from homeassistant.const import ATTR_ENTITY_ID
|
|
from . import Helper
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
BROADLINK_CONTROLLER = 'Broadlink'
|
|
XIAOMI_CONTROLLER = 'Xiaomi'
|
|
MQTT_CONTROLLER = 'MQTT'
|
|
LOOKIN_CONTROLLER = 'LOOKin'
|
|
ESPHOME_CONTROLLER = 'ESPHome'
|
|
|
|
ENC_BASE64 = 'Base64'
|
|
ENC_HEX = 'Hex'
|
|
ENC_PRONTO = 'Pronto'
|
|
ENC_RAW = 'Raw'
|
|
|
|
BROADLINK_COMMANDS_ENCODING = [ENC_BASE64, ENC_HEX, ENC_PRONTO]
|
|
XIAOMI_COMMANDS_ENCODING = [ENC_PRONTO, ENC_RAW]
|
|
MQTT_COMMANDS_ENCODING = [ENC_RAW]
|
|
LOOKIN_COMMANDS_ENCODING = [ENC_PRONTO, ENC_RAW]
|
|
ESPHOME_COMMANDS_ENCODING = [ENC_RAW]
|
|
|
|
|
|
def get_controller(hass, controller, encoding, controller_data, delay):
|
|
"""Return a controller compatible with the specification provided."""
|
|
controllers = {
|
|
BROADLINK_CONTROLLER: BroadlinkController,
|
|
XIAOMI_CONTROLLER: XiaomiController,
|
|
MQTT_CONTROLLER: MQTTController,
|
|
LOOKIN_CONTROLLER: LookinController,
|
|
ESPHOME_CONTROLLER: ESPHomeController
|
|
}
|
|
try:
|
|
return controllers[controller](hass, controller, encoding, controller_data, delay)
|
|
except KeyError:
|
|
raise Exception("The controller is not supported.")
|
|
|
|
|
|
class AbstractController(ABC):
|
|
"""Representation of a controller."""
|
|
def __init__(self, hass, controller, encoding, controller_data, delay):
|
|
self.check_encoding(encoding)
|
|
self.hass = hass
|
|
self._controller = controller
|
|
self._encoding = encoding
|
|
self._controller_data = controller_data
|
|
self._delay = delay
|
|
|
|
@abstractmethod
|
|
def check_encoding(self, encoding):
|
|
"""Check if the encoding is supported by the controller."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def send(self, command):
|
|
"""Send a command."""
|
|
pass
|
|
|
|
|
|
class BroadlinkController(AbstractController):
|
|
"""Controls a Broadlink device."""
|
|
|
|
def check_encoding(self, encoding):
|
|
"""Check if the encoding is supported by the controller."""
|
|
if encoding not in BROADLINK_COMMANDS_ENCODING:
|
|
raise Exception("The encoding is not supported "
|
|
"by the Broadlink controller.")
|
|
|
|
async def send(self, command):
|
|
"""Send a command."""
|
|
commands = []
|
|
|
|
if not isinstance(command, list):
|
|
command = [command]
|
|
|
|
for _command in command:
|
|
if self._encoding == ENC_HEX:
|
|
try:
|
|
_command = binascii.unhexlify(_command)
|
|
_command = b64encode(_command).decode('utf-8')
|
|
except:
|
|
raise Exception("Error while converting "
|
|
"Hex to Base64 encoding")
|
|
|
|
if self._encoding == ENC_PRONTO:
|
|
try:
|
|
_command = _command.replace(' ', '')
|
|
_command = bytearray.fromhex(_command)
|
|
_command = Helper.pronto2lirc(_command)
|
|
_command = Helper.lirc2broadlink(_command)
|
|
_command = b64encode(_command).decode('utf-8')
|
|
except:
|
|
raise Exception("Error while converting "
|
|
"Pronto to Base64 encoding")
|
|
|
|
commands.append('b64:' + _command)
|
|
|
|
service_data = {
|
|
ATTR_ENTITY_ID: self._controller_data,
|
|
'command': commands,
|
|
'delay_secs': self._delay
|
|
}
|
|
|
|
await self.hass.services.async_call(
|
|
'remote', 'send_command', service_data)
|
|
|
|
|
|
class XiaomiController(AbstractController):
|
|
"""Controls a Xiaomi device."""
|
|
|
|
def check_encoding(self, encoding):
|
|
"""Check if the encoding is supported by the controller."""
|
|
if encoding not in XIAOMI_COMMANDS_ENCODING:
|
|
raise Exception("The encoding is not supported "
|
|
"by the Xiaomi controller.")
|
|
|
|
async def send(self, command):
|
|
"""Send a command."""
|
|
service_data = {
|
|
ATTR_ENTITY_ID: self._controller_data,
|
|
'command': self._encoding.lower() + ':' + command
|
|
}
|
|
|
|
await self.hass.services.async_call(
|
|
'remote', 'send_command', service_data)
|
|
|
|
|
|
class MQTTController(AbstractController):
|
|
"""Controls a MQTT device."""
|
|
|
|
def check_encoding(self, encoding):
|
|
"""Check if the encoding is supported by the controller."""
|
|
if encoding not in MQTT_COMMANDS_ENCODING:
|
|
raise Exception("The encoding is not supported "
|
|
"by the mqtt controller.")
|
|
|
|
async def send(self, command):
|
|
"""Send a command."""
|
|
service_data = {
|
|
'topic': self._controller_data,
|
|
'payload': command
|
|
}
|
|
|
|
await self.hass.services.async_call(
|
|
'mqtt', 'publish', service_data)
|
|
|
|
|
|
class LookinController(AbstractController):
|
|
"""Controls a Lookin device."""
|
|
|
|
def check_encoding(self, encoding):
|
|
"""Check if the encoding is supported by the controller."""
|
|
if encoding not in LOOKIN_COMMANDS_ENCODING:
|
|
raise Exception("The encoding is not supported "
|
|
"by the LOOKin controller.")
|
|
|
|
async def send(self, command):
|
|
"""Send a command."""
|
|
encoding = self._encoding.lower().replace('pronto', 'prontohex')
|
|
url = f"http://{self._controller_data}/commands/ir/" \
|
|
f"{encoding}/{command}"
|
|
await self.hass.async_add_executor_job(requests.get, url)
|
|
|
|
|
|
class ESPHomeController(AbstractController):
|
|
"""Controls a ESPHome device."""
|
|
|
|
def check_encoding(self, encoding):
|
|
"""Check if the encoding is supported by the controller."""
|
|
if encoding not in ESPHOME_COMMANDS_ENCODING:
|
|
raise Exception("The encoding is not supported "
|
|
"by the ESPHome controller.")
|
|
|
|
async def send(self, command):
|
|
"""Send a command."""
|
|
service_data = {'command': json.loads(command)}
|
|
|
|
await self.hass.services.async_call(
|
|
'esphome', self._controller_data, service_data) |