Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

removed integration own auto updates implementation - use HACS instead #6

Merged
merged 4 commits into from
May 20, 2024
Merged
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
200 changes: 1 addition & 199 deletions custom_components/smartir/__init__.py
Original file line number Diff line number Diff line change
@@ -1,199 +1 @@
import aiofiles
import aiohttp
import asyncio
import binascii
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
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType

_LOGGER = logging.getLogger(__name__)

DOMAIN = "smartir"
VERSION = "1.17.9"
MANIFEST_URL = (
"https://raw.githubusercontent.com/"
"smartHomeHub/SmartIR/{}/"
"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__))

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."""
conf = config.get(DOMAIN)

if conf is None:
return True

check_updates = conf[CONF_CHECK_UPDATES]
update_branch = conf[CONF_UPDATE_BRANCH]

async def _check_updates(service):
await _update(hass, update_branch)

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)

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"]

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",
)
return

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",
)
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",
)
return

# Begin update
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)
await Helper.downloader(source, dest)
except Exception:
has_errors = True
_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",
)
else:
hass.components.persistent_notification.async_create(
"Successfully updated to {}. Please restart Home Assistant.".format(
last_version
),
title="SmartIR",
)
except Exception:
_LOGGER.error("An error occurred while checking for updates.")


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:
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)
]

if codes[0]:
raise ValueError("Pronto code should start with 0000")
if len(codes) != 4 + 2 * (codes[2] + codes[3]):
raise ValueError("Number of pulse widths does not match the preamble")

frequency = 1 / (codes[1] * 0.241246)
return [int(round(code / frequency)) for code in codes[4:]]

@staticmethod
def lirc2broadlink(pulses):
array = bytearray()

for pulse in pulses:
pulse = int(pulse * 269 / 8192)

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

packet = bytearray([0x26, 0x00])
packet += bytearray(struct.pack("<H", len(array)))
packet += array
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
"""The SmartIR component."""
35 changes: 8 additions & 27 deletions custom_components/smartir/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from homeassistant.helpers.event import async_track_state_change_event
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.restore_state import RestoreEntity
from . import COMPONENT_ABS_DIR, Helper
from .controller import get_controller

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -67,41 +66,23 @@
)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
async def async_setup_platform(
hass: HomeAssistant, config: ConfigType, async_add_entities, discovery_info=None
):
"""Set up the IR Climate platform."""
_LOGGER.debug("Setting up the startir platform")
device_code = config.get(CONF_DEVICE_CODE)
device_files_subdir = os.path.join("codes", "climate")
device_files_absdir = os.path.join(COMPONENT_ABS_DIR, device_files_subdir)

if not os.path.isdir(device_files_absdir):
os.makedirs(device_files_absdir)
device_files_absdir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), device_files_subdir
)

device_json_filename = str(device_code) + ".json"
device_json_path = os.path.join(device_files_absdir, device_json_filename)

if not os.path.exists(device_json_path):
_LOGGER.warning(
"Couldn't find the device Json file. The component will "
"try to download it from the GitHub repo."
)

try:
codes_source = (
"https://raw.githubusercontent.com/"
"smartHomeHub/SmartIR/master/"
"codes/climate/{}.json"
)

await Helper.downloader(codes_source.format(device_code), device_json_path)
except Exception:
_LOGGER.error(
"There was an error while downloading the device Json file. "
"Please check your internet connection or if the device code "
"exists on GitHub. If the problem still exists please "
"place the file manually in the proper directory."
)
return
_LOGGER.error("Couldn't find the device Json file %s!", device_json_filename)
return

with open(device_json_path) as j:
try:
Expand Down
45 changes: 44 additions & 1 deletion custom_components/smartir/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import binascii
import requests
import logging
import struct
import json

from homeassistant.const import ATTR_ENTITY_ID
from . import Helper

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -184,3 +184,46 @@ async def send(self, command):
await self.hass.services.async_call(
"esphome", self._controller_data, service_data
)


class Helper:
"""Static shared functions."""

@staticmethod
def pronto2lirc(pronto):
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")
if len(codes) != 4 + 2 * (codes[2] + codes[3]):
raise ValueError("Number of pulse widths does not match the preamble")

frequency = 1 / (codes[1] * 0.241246)
return [int(round(code / frequency)) for code in codes[4:]]

@staticmethod
def lirc2broadlink(pulses):
array = bytearray()

for pulse in pulses:
pulse = int(pulse * 269 / 8192)

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

packet = bytearray([0x26, 0x00])
packet += bytearray(struct.pack("<H", len(array)))
packet += array
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
34 changes: 10 additions & 24 deletions custom_components/smartir/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
ordered_list_item_to_percentage,
percentage_to_ordered_list_item,
)
from . import COMPONENT_ABS_DIR, Helper
from .controller import get_controller

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -52,11 +51,15 @@
)


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
async def async_setup_platform(
hass: HomeAssistant, config: ConfigType, async_add_entities, discovery_info=None
):
"""Set up the IR Fan platform."""
device_code = config.get(CONF_DEVICE_CODE)
device_files_subdir = os.path.join("codes", "fan")
device_files_absdir = os.path.join(COMPONENT_ABS_DIR, device_files_subdir)
device_files_absdir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), device_files_subdir
)

if not os.path.isdir(device_files_absdir):
os.makedirs(device_files_absdir)
Expand All @@ -65,31 +68,14 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
device_json_path = os.path.join(device_files_absdir, device_json_filename)

if not os.path.exists(device_json_path):
_LOGGER.warning(
"Couldn't find the device Json file. The component will "
"try to download it from the GitHub repo."
)

try:
codes_source = (
"https://raw.githubusercontent.com/"
"smartHomeHub/SmartIR/master/"
"codes/fan/{}.json"
)

await Helper.downloader(codes_source.format(device_code), device_json_path)
except Exception:
_LOGGER.error(
"There was an error while downloading the device Json file. "
"Please check your internet connection or if the device code "
"exists on GitHub. If the problem still exists please "
"place the file manually in the proper directory."
)
return
_LOGGER.error("Couldn't find the device Json file %s!", device_json_filename)
return

with open(device_json_path) as j:
try:
_LOGGER.debug(f"loading json file {device_json_path}")
device_data = json.load(j)
_LOGGER.debug(f"{device_json_path} file loaded")
except Exception:
_LOGGER.error("The device JSON file is invalid")
return
Expand Down
Loading
Loading