Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 76 additions & 48 deletions custom_components/smartir/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,49 @@
from distutils.version import StrictVersion
import json
import logging
import os.path
import requests
import struct
import voluptuous as vol

from aiohttp import ClientSession
from homeassistant.const import (
ATTR_FRIENDLY_NAME, __version__ as current_ha_version)
from homeassistant.const import ATTR_FRIENDLY_NAME, __version__ as current_ha_version
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType
from pathlib import Path

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'smartir'
VERSION = '1.18.1'
DOMAIN = "smartir"
VERSION = "1.18.2"
MANIFEST_URL = (
"https://raw.githubusercontent.com/"
"smartHomeHub/SmartIR/{}/"
"custom_components/smartir/manifest.json")
"custom_components/smartir/manifest.json"
)
REMOTE_BASE_URL = (
"https://raw.githubusercontent.com/"
"smartHomeHub/SmartIR/{}/"
"custom_components/smartir/")
COMPONENT_ABS_DIR = os.path.dirname(
os.path.abspath(__file__))
"custom_components/smartir/"
)
COMPONENT_ABS_DIR = Path(__file__).parent.resolve()

CONF_CHECK_UPDATES = "check_updates"
CONF_UPDATE_BRANCH = "update_branch"

CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_CHECK_UPDATES, default=True): cv.boolean,
vol.Optional(CONF_UPDATE_BRANCH, default="master"): vol.In(
["master", "rc"]
),
}
)
},
extra=vol.ALLOW_EXTRA,
)

CONF_CHECK_UPDATES = 'check_updates'
CONF_UPDATE_BRANCH = 'update_branch'

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Optional(CONF_CHECK_UPDATES, default=True): cv.boolean,
vol.Optional(CONF_UPDATE_BRANCH, default='master'): vol.In(
['master', 'rc'])
})
}, extra=vol.ALLOW_EXTRA)

async def async_setup(hass, config):
"""Set up the SmartIR component."""
Expand All @@ -58,85 +65,106 @@ async def _check_updates(service):
async def _update_component(service):
await _update(hass, update_branch, True)

hass.services.async_register(DOMAIN, 'check_updates', _check_updates)
hass.services.async_register(DOMAIN, 'update_component', _update_component)
hass.services.async_register(DOMAIN, "check_updates", _check_updates)
hass.services.async_register(DOMAIN, "update_component", _update_component)

if check_updates:
await _update(hass, update_branch, False, False)

return True


async def _update(hass, branch, do_update=False, notify_if_latest=True):
try:
async with aiohttp.ClientSession() as session:
async with session.get(MANIFEST_URL.format(branch)) as response:
if response.status == 200:
data = await response.json(content_type='text/plain')
min_ha_version = data['homeassistant']
last_version = data['updater']['version']
release_notes = data['updater']['releaseNotes']

data = await response.json(content_type="text/plain")
min_ha_version = data["homeassistant"]
last_version = data["updater"]["version"]
release_notes = data["updater"]["releaseNotes"]

if StrictVersion(last_version) <= StrictVersion(VERSION):
if notify_if_latest:
hass.components.persistent_notification.async_create(
"You're already using the latest version!",
title='SmartIR')
"You're already using the latest version!",
title="SmartIR",
)
return

if StrictVersion(current_ha_version) < StrictVersion(min_ha_version):
if StrictVersion(current_ha_version) < StrictVersion(
min_ha_version
):
hass.components.persistent_notification.async_create(
"There is a new version of SmartIR integration, but it is **incompatible** "
"with your system. Please first update Home Assistant.", title='SmartIR')
"with your system. Please first update Home Assistant.",
title="SmartIR",
)
return

if do_update is False:
hass.components.persistent_notification.async_create(
"A new version of SmartIR integration is available ({}). "
"Call the ``smartir.update_component`` service to update "
"the integration. \n\n **Release notes:** \n{}"
.format(last_version, release_notes), title='SmartIR')
"the integration. \n\n **Release notes:** \n{}".format(
last_version, release_notes
),
title="SmartIR",
)
return

# Begin update
files = data['updater']['files']
files = data["updater"]["files"]
has_errors = False

for file in files:
try:
source = REMOTE_BASE_URL.format(branch) + file
dest = os.path.join(COMPONENT_ABS_DIR, file)
os.makedirs(os.path.dirname(dest), exist_ok=True)
dest = COMPONENT_ABS_DIR / file
dest.parent.mkdir(parents=True, exist_ok=True)
await Helper.downloader(source, dest)
except Exception:
has_errors = True
_LOGGER.error("Error updating %s. Please update the file manually.", file)
_LOGGER.error(
"Error updating %s. Please update the file manually.",
file,
)

if has_errors:
hass.components.persistent_notification.async_create(
"There was an error updating one or more files of SmartIR. "
"Please check the logs for more information.", title='SmartIR')
"Please check the logs for more information.",
title="SmartIR",
)
else:
hass.components.persistent_notification.async_create(
"Successfully updated to {}. Please restart Home Assistant."
.format(last_version), title='SmartIR')
"Successfully updated to {}. Please restart Home Assistant.".format(
last_version
),
title="SmartIR",
)
except Exception:
_LOGGER.error("An error occurred while checking for updates.")
_LOGGER.error("An error occurred while checking for updates.")


class Helper():
class Helper:
@staticmethod
async def downloader(source, dest):
async with aiohttp.ClientSession() as session:
async with session.get(source) as response:
if response.status == 200:
async with aiofiles.open(dest, mode='wb') as f:
async with aiofiles.open(str(dest), mode="wb") as f:
await f.write(await response.read())
else:
raise Exception("File not found")

@staticmethod
def pronto2lirc(pronto):
codes = [int(binascii.hexlify(pronto[i:i+2]), 16) for i in range(0, len(pronto), 2)]
codes = [
int(binascii.hexlify(pronto[i : i + 2]), 16)
for i in range(0, len(pronto), 2)
]

if codes[0]:
raise ValueError("Pronto code should start with 0000")
Expand All @@ -154,18 +182,18 @@ def lirc2broadlink(pulses):
pulse = int(pulse * 269 / 8192)

if pulse < 256:
array += bytearray(struct.pack('>B', pulse))
array += bytearray(struct.pack(">B", pulse))
else:
array += bytearray([0x00])
array += bytearray(struct.pack('>H', pulse))
array += bytearray(struct.pack(">H", pulse))

packet = bytearray([0x26, 0x00])
packet += bytearray(struct.pack('<H', len(array)))
packet += bytearray(struct.pack("<H", len(array)))
packet += array
packet += bytearray([0x0d, 0x05])
packet += bytearray([0x0D, 0x05])

# Add 0s to make ultimate packet size a multiple of 16 for 128-bit AES encryption.
remainder = (len(packet) + 4) % 16
if remainder:
packet += bytearray(16 - remainder)
return packet
return packet
Loading