Formating

This commit is contained in:
ludeeus
2019-06-16 10:27:05 +02:00
parent af8505d8a3
commit f87e491989
22 changed files with 444 additions and 193 deletions

View File

@ -49,7 +49,7 @@ DOMAIN = "{}".format(NAME_SHORT.lower())
# TODO: Remove this when minimum HA version is > 0.93 # TODO: Remove this when minimum HA version is > 0.93
REQUIREMENTS = ["aiofiles"] REQUIREMENTS = ["aiofiles"]
_LOGGER = logging.getLogger('custom_components.hacs') _LOGGER = logging.getLogger("custom_components.hacs")
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema({vol.Required("token"): cv.string})}, extra=vol.ALLOW_EXTRA {DOMAIN: vol.Schema({vol.Required("token"): cv.string})}, extra=vol.ALLOW_EXTRA
@ -65,7 +65,7 @@ async def async_setup(hass, config): # pylint: disable=unused-argument
# Configure HACS # Configure HACS
await configure_hacs(hass, github_token, config_dir) await configure_hacs(hass, github_token, config_dir)
# Check if custom_updater exists # Check if custom_updater exists
for location in CUSTOM_UPDATER_LOCATIONS: for location in CUSTOM_UPDATER_LOCATIONS:
if os.path.exists(location.format(config_dir)): if os.path.exists(location.format(config_dir)):
msg = CUSTOM_UPDATER_WARNING.format(location.format(config_dir)) msg = CUSTOM_UPDATER_WARNING.format(location.format(config_dir))
@ -73,12 +73,14 @@ async def async_setup(hass, config): # pylint: disable=unused-argument
return False return False
# Check if HA is the required version. # Check if HA is the required version.
if parse_version(HAVERSION) < parse_version('0.92.0'): if parse_version(HAVERSION) < parse_version("0.92.0"):
_LOGGER.critical("You need HA version 92 or newer to use this integration.") _LOGGER.critical("You need HA version 92 or newer to use this integration.")
return False return False
# Add sensor # Add sensor
hass.async_create_task(discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config[DOMAIN])) hass.async_create_task(
discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config[DOMAIN])
)
# Setup startup tasks # Setup startup tasks
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, hacs().startup_tasks()) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, hacs().startup_tasks())
@ -95,7 +97,7 @@ async def async_setup(hass, config): # pylint: disable=unused-argument
# Add to sidepanel # Add to sidepanel
# TODO: Remove this check when minimum HA version is > 0.94 # TODO: Remove this check when minimum HA version is > 0.94
if parse_version(HAVERSION) < parse_version('0.93.9'): if parse_version(HAVERSION) < parse_version("0.93.9"):
await hass.components.frontend.async_register_built_in_panel( await hass.components.frontend.async_register_built_in_panel(
"iframe", "iframe",
IFRAME["title"], IFRAME["title"],
@ -127,7 +129,9 @@ async def configure_hacs(hass, github_token, hass_config_dir):
hacs.migration = HacsMigration() hacs.migration = HacsMigration()
hacs.storage = HacsStorage() hacs.storage = HacsStorage()
hacs.aiogithub = AIOGitHub(github_token, hass.loop, async_create_clientsession(hass)) hacs.aiogithub = AIOGitHub(
github_token, hass.loop, async_create_clientsession(hass)
)
hacs.hass = hass hacs.hass = hass
hacs.config_dir = hass_config_dir hacs.config_dir = hass_config_dir

View File

@ -9,12 +9,13 @@ from aiohttp import ClientError
import backoff import backoff
_LOGGER = logging.getLogger('custom_components.hacs.aiogithub') _LOGGER = logging.getLogger("custom_components.hacs.aiogithub")
class AIOGitHubException(BaseException): class AIOGitHubException(BaseException):
"""Raise this when something is off.""" """Raise this when something is off."""
class AIOGitHub(object): class AIOGitHub(object):
"""Base Github API implementation.""" """Base Github API implementation."""
@ -70,7 +71,9 @@ class AIOGitHub(object):
repositories = [] repositories = []
for repository in response: for repository in response:
repositories.append(AIOGithubRepository(repository, self.token, self.loop, self.session)) repositories.append(
AIOGithubRepository(repository, self.token, self.loop, self.session)
)
return repositories return repositories
@ -103,7 +106,6 @@ class AIOGithubRepository(AIOGitHub):
self.attributes = attributes self.attributes = attributes
self._last_commit = None self._last_commit = None
@property @property
def id(self): def id(self):
return self.attributes.get("id") return self.attributes.get("id")
@ -165,7 +167,9 @@ class AIOGithubRepository(AIOGitHub):
@backoff.on_exception(backoff.expo, ClientError, max_tries=3) @backoff.on_exception(backoff.expo, ClientError, max_tries=3)
async def get_releases(self, latest=False): async def get_releases(self, latest=False):
"""Retrun a list of repository release objects.""" """Retrun a list of repository release objects."""
endpoint = "/repos/" + self.full_name + "/releases/" + "latest" if latest else "" endpoint = (
"/repos/" + self.full_name + "/releases/" + "latest" if latest else ""
)
url = self.baseapi + endpoint url = self.baseapi + endpoint
async with async_timeout.timeout(20, loop=self.loop): async with async_timeout.timeout(20, loop=self.loop):
@ -198,7 +202,8 @@ class AIOGithubRepository(AIOGitHub):
if response.get("message"): if response.get("message"):
raise AIOGitHubException("No commits") raise AIOGitHubException("No commits")
self._last_commit = response['sha'][0:7] self._last_commit = response["sha"][0:7]
class AIOGithubRepositoryContent(AIOGitHub): class AIOGithubRepositoryContent(AIOGitHub):
"""Repository Conetent Github API implementation.""" """Repository Conetent Github API implementation."""
@ -225,15 +230,20 @@ class AIOGithubRepositoryContent(AIOGitHub):
@property @property
def content(self): def content(self):
return base64.b64decode(bytearray(self.attributes.get("content"), "utf-8")).decode() return base64.b64decode(
bytearray(self.attributes.get("content"), "utf-8")
).decode()
@property @property
def download_url(self): def download_url(self):
return self.attributes.get("download_url") or self.attributes.get("browser_download_url") return self.attributes.get("download_url") or self.attributes.get(
"browser_download_url"
)
class AIOGithubRepositoryRelease(AIOGitHub): class AIOGithubRepositoryRelease(AIOGitHub):
"""Repository Release Github API implementation.""" """Repository Release Github API implementation."""
def __init__(self, attributes): def __init__(self, attributes):
"""Initialize.""" """Initialize."""
self.attributes = attributes self.attributes = attributes
@ -248,7 +258,9 @@ class AIOGithubRepositoryRelease(AIOGitHub):
@property @property
def published_at(self): def published_at(self):
return datetime.strptime(self.attributes.get("published_at"), "%Y-%m-%dT%H:%M:%SZ") return datetime.strptime(
self.attributes.get("published_at"), "%Y-%m-%dT%H:%M:%SZ"
)
@property @property
def draft(self): def draft(self):

View File

@ -73,9 +73,7 @@ ERROR = [
################################ ################################
DEFAULT_REPOSITORIES = { DEFAULT_REPOSITORIES = {
"integration": [ "integration": ["StyraHem/ShellyForHASS"],
"StyraHem/ShellyForHASS"
],
"plugin": [ "plugin": [
"maykar/compact-custom-header", "maykar/compact-custom-header",
"maykar/lovelace-swipe-navigation", "maykar/lovelace-swipe-navigation",
@ -87,5 +85,5 @@ DEFAULT_REPOSITORIES = {
"finity69x2/fan-control-entity-row", "finity69x2/fan-control-entity-row",
"thomasloven/lovelace-card-mod", "thomasloven/lovelace-card-mod",
"thomasloven/lovelace-markdown-mod", "thomasloven/lovelace-markdown-mod",
] ],
} }

View File

@ -1,8 +1,10 @@
"""Custom Exceptions.""" """Custom Exceptions."""
class HacsBaseException(Exception): class HacsBaseException(Exception):
"""Super basic.""" """Super basic."""
class HacsUserScrewupException(HacsBaseException): class HacsUserScrewupException(HacsBaseException):
"""Raise this when the user does something they should not do.""" """Raise this when the user does something they should not do."""
@ -14,6 +16,7 @@ class HacsNotSoBasicException(HacsBaseException):
class HacsDataFileMissing(HacsBaseException): class HacsDataFileMissing(HacsBaseException):
"""Raise this storage datafile is missing.""" """Raise this storage datafile is missing."""
class HacsDataNotExpected(HacsBaseException): class HacsDataNotExpected(HacsBaseException):
"""Raise this when data returned from storage is not ok.""" """Raise this when data returned from storage is not ok."""
@ -21,17 +24,22 @@ class HacsDataNotExpected(HacsBaseException):
class HacsRepositoryInfo(HacsBaseException): class HacsRepositoryInfo(HacsBaseException):
"""Raise this when repository info is missing/wrong.""" """Raise this when repository info is missing/wrong."""
class HacsRequirement(HacsBaseException): class HacsRequirement(HacsBaseException):
"""Raise this when repository is missing a requirement.""" """Raise this when repository is missing a requirement."""
class HacsMissingManifest(HacsBaseException): class HacsMissingManifest(HacsBaseException):
"""Raise this when manifest is missing.""" """Raise this when manifest is missing."""
def __init__(self, message="The manifest file is missing in the repository."): def __init__(self, message="The manifest file is missing in the repository."):
super().__init__(message) super().__init__(message)
self.message = message self.message = message
class HacsBlacklistException(HacsBaseException): class HacsBlacklistException(HacsBaseException):
"""Raise this when the repository is currently in the blacklist.""" """Raise this when the repository is currently in the blacklist."""
def __init__(self, message="The repository is currently in the blacklist."): def __init__(self, message="The repository is currently in the blacklist."):
super().__init__(message) super().__init__(message)
self.message = message self.message = message

View File

@ -4,7 +4,7 @@ import logging
from aiohttp import web from aiohttp import web
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
class HacsAPIView(HacsViewBase): class HacsAPIView(HacsViewBase):
@ -16,7 +16,9 @@ class HacsAPIView(HacsViewBase):
"""Initilize.""" """Initilize."""
self.url = self.url_path["api"] + r"/{element}/{action}" self.url = self.url_path["api"] + r"/{element}/{action}"
async def get(self, request, element, action=None): # pylint: disable=unused-argument async def get(
self, request, element, action=None
): # pylint: disable=unused-argument
"""Serve HacsAPIView.""" """Serve HacsAPIView."""
_LOGGER.debug("GET API call for %s with %s", element, action) _LOGGER.debug("GET API call for %s with %s", element, action)
@ -26,43 +28,47 @@ class HacsAPIView(HacsViewBase):
await repository.install() await repository.install()
await self.storage.set() await self.storage.set()
if action == "172733314": if action == "172733314":
raise web.HTTPFound(self.url_path['settings']) raise web.HTTPFound(self.url_path["settings"])
raise web.HTTPFound("{}/{}".format(self.url_path['repository'], repository.repository_id)) raise web.HTTPFound(
"{}/{}".format(self.url_path["repository"], repository.repository_id)
)
# Update a repository # Update a repository
elif element == "repository_update_repository": elif element == "repository_update_repository":
repository = self.repositories[action] repository = self.repositories[action]
await repository.update() await repository.update()
await self.storage.set() await self.storage.set()
raise web.HTTPFound("{}/{}".format(self.url_path['repository'], repository.repository_id)) raise web.HTTPFound(
"{}/{}".format(self.url_path["repository"], repository.repository_id)
)
# Update a repository # Update a repository
elif element == "repository_update_settings": elif element == "repository_update_settings":
repository = self.repositories[action] repository = self.repositories[action]
await repository.update() await repository.update()
await self.storage.set() await self.storage.set()
raise web.HTTPFound(self.url_path['settings']) raise web.HTTPFound(self.url_path["settings"])
# Uninstall a element from the repository view # Uninstall a element from the repository view
elif element == "repository_uninstall": elif element == "repository_uninstall":
repository = self.repositories[action] repository = self.repositories[action]
await repository.uninstall() await repository.uninstall()
await self.storage.set() await self.storage.set()
raise web.HTTPFound(self.url_path['store']) raise web.HTTPFound(self.url_path["store"])
# Remove a custom repository from the settings view # Remove a custom repository from the settings view
elif element == "repository_remove": elif element == "repository_remove":
repository = self.repositories[action] repository = self.repositories[action]
await repository.remove() await repository.remove()
await self.storage.set() await self.storage.set()
raise web.HTTPFound(self.url_path['settings']) raise web.HTTPFound(self.url_path["settings"])
# Hide a repository. # Hide a repository.
elif element == "repository_hide": elif element == "repository_hide":
repository = self.repositories[action] repository = self.repositories[action]
repository.hide = True repository.hide = True
await self.storage.set() await self.storage.set()
raise web.HTTPFound(self.url_path['store']) raise web.HTTPFound(self.url_path["store"])
# Unhide a repository. # Unhide a repository.
elif element == "repository_unhide": elif element == "repository_unhide":
@ -70,21 +76,17 @@ class HacsAPIView(HacsViewBase):
repository.hide = False repository.hide = False
await repository.update() await repository.update()
await self.storage.set() await self.storage.set()
raise web.HTTPFound(self.url_path['settings']) raise web.HTTPFound(self.url_path["settings"])
# Remove a custom repository from the settings view # Remove a custom repository from the settings view
elif element == "repositories_reload": elif element == "repositories_reload":
self.hass.async_create_task(self.update_repositories("Run it!")) self.hass.async_create_task(self.update_repositories("Run it!"))
raise web.HTTPFound(self.url_path['settings']) raise web.HTTPFound(self.url_path["settings"])
# Show content of hacs # Show content of hacs
elif element == "hacs" and action == "inspect": elif element == "hacs" and action == "inspect":
jsons = {} jsons = {}
skip = [ skip = ["content_objects", "last_release_object", "repository"]
"content_objects",
"last_release_object",
"repository",
]
for repository in self.repositories: for repository in self.repositories:
repository = self.repositories[repository] repository = self.repositories[repository]
jsons[repository.repository_id] = {} jsons[repository.repository_id] = {}
@ -97,13 +99,16 @@ class HacsAPIView(HacsViewBase):
elif element == "log" and action == "get": elif element == "log" and action == "get":
from ...handler.log import get_log_file_content from ...handler.log import get_log_file_content
content = self.base_content content = self.base_content
content += await get_log_file_content(self.config_dir) content += await get_log_file_content(self.config_dir)
return web.Response(body=content, content_type="text/html", charset="utf-8") return web.Response(body=content, content_type="text/html", charset="utf-8")
raise web.HTTPFound(self.url_path['error']) raise web.HTTPFound(self.url_path["error"])
async def post(self, request, element, action=None): # pylint: disable=unused-argument async def post(
self, request, element, action=None
): # pylint: disable=unused-argument
"""Prosess POST API actions.""" """Prosess POST API actions."""
_LOGGER.debug("GET POST call for %s with %s", element, action) _LOGGER.debug("GET POST call for %s with %s", element, action)
@ -111,16 +116,20 @@ class HacsAPIView(HacsViewBase):
if element == "repository_register": if element == "repository_register":
repository_name = postdata["custom_url"] repository_name = postdata["custom_url"]
if 'repository_type' in postdata: if "repository_type" in postdata:
repository_type = postdata["repository_type"] repository_type = postdata["repository_type"]
_LOGGER.debug("GET POST call for %s with %s", repository_name, repository_type) _LOGGER.debug(
"GET POST call for %s with %s", repository_name, repository_type
)
# Stip first part if it's an URL. # Stip first part if it's an URL.
if "https://github" in repository_name: if "https://github" in repository_name:
repository_name = repository_name.split("https://github.com/")[-1] repository_name = repository_name.split("https://github.com/")[-1]
if "https://www.github" in repository_name: if "https://www.github" in repository_name:
repository_name = repository_name.split("https://www.github.com/")[-1] repository_name = repository_name.split("https://www.github.com/")[
-1
]
# Strip whitespace # Strip whitespace
repository_name = repository_name.split()[0] repository_name = repository_name.split()[0]
@ -129,11 +138,21 @@ class HacsAPIView(HacsViewBase):
if repository_name != "": if repository_name != "":
if repository_name in self.blacklist: if repository_name in self.blacklist:
self.blacklist.remove(repository_name) self.blacklist.remove(repository_name)
repository, result = await self.register_new_repository(repository_type, repository_name) repository, result = await self.register_new_repository(
repository_type, repository_name
)
if result: if result:
await self.storage.set() await self.storage.set()
raise web.HTTPFound("{}/{}".format(self.url_path['repository'], repository.repository_id)) raise web.HTTPFound(
"{}/{}".format(
self.url_path["repository"], repository.repository_id
)
)
message = "Could not add {} at this time, check the log for more details.".format(repository_name) message = "Could not add {} at this time, check the log for more details.".format(
repository_name
)
raise web.HTTPFound("{}?message={}".format(self.url_path['settings'], message)) raise web.HTTPFound(
"{}?message={}".format(self.url_path["settings"], message)
)

View File

@ -10,7 +10,8 @@ from aiohttp import web
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
from ...const import ERROR, ISSUE_URL from ...const import ERROR, ISSUE_URL
_LOGGER = logging.getLogger('custom_components.hacs..frontend') _LOGGER = logging.getLogger("custom_components.hacs..frontend")
class HacsErrorView(HacsViewBase): class HacsErrorView(HacsViewBase):
"""Serve error.""" """Serve error."""
@ -30,31 +31,34 @@ class HacsErrorView(HacsViewBase):
stack_trace = list() stack_trace = list()
for trace in trace_back: for trace in trace_back:
stack_trace.append("File : {} , Line : {}, Func.Name : {}, Message : {}", format( stack_trace.append(
trace[0], trace[1], trace[2], trace[3] "File : {} , Line : {}, Func.Name : {}, Message : {}",
)) format(trace[0], trace[1], trace[2], trace[3]),
)
# HARD styling # HARD styling
stacks = "" stacks = ""
for stack in stack_trace: for stack in stack_trace:
stacks += stack stacks += stack
stacks = stacks.replace("File :", "</br>---------------------------------------------------------------</br><b>File :</b>") stacks = stacks.replace(
"File :",
"</br>---------------------------------------------------------------</br><b>File :</b>",
)
stacks = stacks.replace(", Line :", "</br><b>Line :</b>") stacks = stacks.replace(", Line :", "</br><b>Line :</b>")
stacks = stacks.replace(", Func.Name :", "</br><b>Func.Name :</b>") stacks = stacks.replace(", Func.Name :", "</br><b>Func.Name :</b>")
stacks = stacks.replace(", Message :", "</br><b>Message :</b>")[86:-1] stacks = stacks.replace(", Message :", "</br><b>Message :</b>")[86:-1]
if ex_type is not None: if ex_type is not None:
codeblock = """ codeblock = """
<p><b>Exception type:</b> {}</p> <p><b>Exception type:</b> {}</p>
<p><b>Exception message:</b> {}</p> <p><b>Exception message:</b> {}</p>
<code class="codeblock errorview"">{}</code> <code class="codeblock errorview"">{}</code>
""".format(ex_type.__name__, ex_value, stacks) """.format(
ex_type.__name__, ex_value, stacks
)
else: else:
codeblock = "" codeblock = ""
# Generate content # Generate content
content = self.base_content content = self.base_content
content += """ content += """
@ -74,7 +78,9 @@ class HacsErrorView(HacsViewBase):
<div class='center-align' style='margin-top: 100px'> <div class='center-align' style='margin-top: 100px'>
<img src='https://i.pinimg.com/originals/ec/85/67/ec856744fac64a5a9e407733f190da5a.png'> <img src='https://i.pinimg.com/originals/ec/85/67/ec856744fac64a5a9e407733f190da5a.png'>
</div> </div>
""".format(random.choice(ERROR), codeblock, ISSUE_URL, self.url_path["api"]) """.format(
random.choice(ERROR), codeblock, ISSUE_URL, self.url_path["api"]
)
except Exception as exception: except Exception as exception:
message = "GREAT!, even the error page is broken... ({})".format(exception) message = "GREAT!, even the error page is broken... ({})".format(exception)

View File

@ -5,7 +5,7 @@ from aiohttp import web
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
from ...const import NO_ELEMENTS from ...const import NO_ELEMENTS
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
class HacsOverviewView(HacsViewBase): class HacsOverviewView(HacsViewBase):
@ -32,14 +32,22 @@ class HacsOverviewView(HacsViewBase):
else: else:
for repository in self.repositories_list_name: for repository in self.repositories_list_name:
if not repository.track or repository.hide or not repository.installed: if (
not repository.track
or repository.hide
or not repository.installed
):
continue continue
if repository.pending_restart: if repository.pending_restart:
card_icon = "<i class='fas fa-cube card-status pending-restart'></i>" card_icon = (
"<i class='fas fa-cube card-status pending-restart'></i>"
)
elif repository.pending_update: elif repository.pending_update:
card_icon = "<i class='fas fa-cube card-status pending-update'></i>" card_icon = (
"<i class='fas fa-cube card-status pending-update'></i>"
)
elif repository.installed: elif repository.installed:
card_icon = "<i class='fas fa-cube card-status installed'></i>" card_icon = "<i class='fas fa-cube card-status installed'></i>"
@ -58,7 +66,15 @@ class HacsOverviewView(HacsViewBase):
</span> </span>
</div> </div>
</a> </a>
""".format(self.url_path["repository"], repository.repository_id, repository.topics, repository.authors, card_icon, repository.name, repository.description) """.format(
self.url_path["repository"],
repository.repository_id,
repository.topics,
repository.authors,
card_icon,
repository.name,
repository.description,
)
if repository.repository_type == "integration": if repository.repository_type == "integration":
integrations.append(card) integrations.append(card)
@ -95,4 +111,4 @@ class HacsOverviewView(HacsViewBase):
_LOGGER.error(exception) _LOGGER.error(exception)
raise web.HTTPFound(self.url_path["error"]) raise web.HTTPFound(self.url_path["error"])
return web.Response(body=content, content_type="text/html", charset="utf-8") return web.Response(body=content, content_type="text/html", charset="utf-8")

View File

@ -6,7 +6,7 @@ from aiohttp import web
from aiohttp.web_exceptions import HTTPNotFound from aiohttp.web_exceptions import HTTPNotFound
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
class HacsPluginView(HacsViewBase): class HacsPluginView(HacsViewBase):
@ -25,8 +25,8 @@ class HacsPluginView(HacsViewBase):
file = "{}/www/community/{}".format(self.config_dir, requested_file) file = "{}/www/community/{}".format(self.config_dir, requested_file)
# Serve .gz if it exist # Serve .gz if it exist
if os.path.exists(file + '.gz'): if os.path.exists(file + ".gz"):
file += '.gz' file += ".gz"
response = None response = None
if os.path.exists(file): if os.path.exists(file):
@ -38,7 +38,9 @@ class HacsPluginView(HacsViewBase):
raise HTTPNotFound() raise HTTPNotFound()
except Exception as error: # pylint: disable=broad-except except Exception as error: # pylint: disable=broad-except
_LOGGER.debug("there was an issue trying to serve %s - %s", requested_file, error) _LOGGER.debug(
"there was an issue trying to serve %s - %s", requested_file, error
)
raise HTTPNotFound() raise HTTPNotFound()
return response return response

View File

@ -4,7 +4,7 @@ import logging
from aiohttp import web from aiohttp import web
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
LOVELACE_EXAMLE_URL = """ LOVELACE_EXAMLE_URL = """
<pre id="LovelaceExample" class="yaml"> <pre id="LovelaceExample" class="yaml">
@ -23,6 +23,7 @@ LOVELACE_EXAMLE_URL_TYPE = """
</pre> </pre>
""" """
class HacsRepositoryView(HacsViewBase): class HacsRepositoryView(HacsViewBase):
"""Serve HacsRepositoryView.""" """Serve HacsRepositoryView."""
@ -53,11 +54,12 @@ class HacsRepositoryView(HacsViewBase):
</div> </div>
</div> </div>
</div> </div>
""".format(message) """.format(
message
)
else: else:
custom_message = "" custom_message = ""
if repository.pending_restart: if repository.pending_restart:
pending_restart = """ pending_restart = """
<div class='container''> <div class='container''>
@ -79,20 +81,14 @@ class HacsRepositoryView(HacsViewBase):
if repository.additional_info: if repository.additional_info:
if repository.info is None: if repository.info is None:
info = "</br>" + await self.aiogithub.render_markdown(repository.additional_info) info = "</br>" + await self.aiogithub.render_markdown(
info = info.replace("<h3>", "<h6>").replace( repository.additional_info
"</h3>", "</h6>"
)
info = info.replace("<h2>", "<h5>").replace(
"</h2>", "</h5>"
)
info = info.replace("<h1>", "<h4>").replace(
"</h1>", "</h4>"
) )
info = info.replace("<h3>", "<h6>").replace("</h3>", "</h6>")
info = info.replace("<h2>", "<h5>").replace("</h2>", "</h5>")
info = info.replace("<h1>", "<h4>").replace("</h1>", "</h4>")
info = info.replace("<code>", "<code class='codeinfo'>") info = info.replace("<code>", "<code class='codeinfo'>")
info = info.replace( info = info.replace("<table>", "<table class='white-text'>")
"<table>", "<table class='white-text'>"
)
info = info.replace( info = info.replace(
'<a href="http', '<a target="_blank" href="http' '<a href="http', '<a target="_blank" href="http'
) )
@ -104,13 +100,14 @@ class HacsRepositoryView(HacsViewBase):
else: else:
info = "" info = ""
if repository.authors: if repository.authors:
authors = "<p>Author(s): " authors = "<p>Author(s): "
for author in repository.authors: for author in repository.authors:
if "@" in author: if "@" in author:
author = author.split("@")[-1] author = author.split("@")[-1]
authors += "<a href='https://github.com/{author}' target='_blank' style='color: var(--primary-color) !important; margin: 2'> @{author}</a>".format(author=author) authors += "<a href='https://github.com/{author}' target='_blank' style='color: var(--primary-color) !important; margin: 2'> @{author}</a>".format(
author=author
)
authors += "</p>" authors += "</p>"
else: else:
authors = "" authors = ""
@ -126,13 +123,21 @@ class HacsRepositoryView(HacsViewBase):
To learn more about how to configure this, To learn more about how to configure this,
click the "REPOSITORY" link below to get to the repository for this integration. click the "REPOSITORY" link below to get to the repository for this integration.
</i> </i>
""".format(repository.local_path) """.format(
repository.local_path
)
else: else:
if repository.javascript_type is None: if repository.javascript_type is None:
llnote = LOVELACE_EXAMLE_URL.format(repository.name, repository.name.replace("lovelace-", "")) llnote = LOVELACE_EXAMLE_URL.format(
repository.name, repository.name.replace("lovelace-", "")
)
jsnote = MISSING_JS_TYPE jsnote = MISSING_JS_TYPE
else: else:
llnote = LOVELACE_EXAMLE_URL_TYPE.format(repository.name, repository.name.replace("lovelace-", ""), repository.javascript_type) llnote = LOVELACE_EXAMLE_URL_TYPE.format(
repository.name,
repository.name.replace("lovelace-", ""),
repository.javascript_type,
)
jsnote = "" jsnote = ""
note = """ note = """
</br><i> </br><i>
@ -150,7 +155,9 @@ class HacsRepositoryView(HacsViewBase):
To learn more about how to configure this, To learn more about how to configure this,
click the "REPOSITORY" link below button to get to the repository for this plugin. click the "REPOSITORY" link below button to get to the repository for this plugin.
</i> </i>
""".format(repository.local_path, llnote, jsnote) """.format(
repository.local_path, llnote, jsnote
)
if not repository.installed: if not repository.installed:
main_action = "INSTALL" main_action = "INSTALL"
@ -167,7 +174,9 @@ class HacsRepositoryView(HacsViewBase):
name = repository.name.split("lovelace-")[-1] name = repository.name.split("lovelace-")[-1]
else: else:
name = repository.name name = repository.name
open_plugin = "<a href='/community_plugin/{}/{}.js' target='_blank' style='color: var(--primary-color) !important'>OPEN PLUGIN</a>".format(repository.name, name) open_plugin = "<a href='/community_plugin/{}/{}.js' target='_blank' style='color: var(--primary-color) !important'>OPEN PLUGIN</a>".format(
repository.name, name
)
else: else:
open_plugin = "" open_plugin = ""
@ -178,37 +187,52 @@ class HacsRepositoryView(HacsViewBase):
if repository.hide: if repository.hide:
hide_option = """ hide_option = """
<li><a class="dropdown-list-item" href="{}/repository_unhide/{}" onclick="ShowProgressBar()">Unhide</a></li> <li><a class="dropdown-list-item" href="{}/repository_unhide/{}" onclick="ShowProgressBar()">Unhide</a></li>
""".format(self.url_path["api"], repository.repository_id) """.format(
self.url_path["api"], repository.repository_id
)
else: else:
hide_option = """ hide_option = """
<li><a class="dropdown-list-item" href="{}/repository_hide/{}" onclick="ShowProgressBar()">Hide</a></li> <li><a class="dropdown-list-item" href="{}/repository_hide/{}" onclick="ShowProgressBar()">Hide</a></li>
""".format(self.url_path["api"], repository.repository_id) """.format(
self.url_path["api"], repository.repository_id
)
content = self.base_content content = self.base_content
if repository.version_installed is not None: if repository.version_installed is not None:
inst_ver = "<p><b>Installed version:</b> {}</p>".format(repository.version_installed) inst_ver = "<p><b>Installed version:</b> {}</p>".format(
repository.version_installed
)
else: else:
if repository.installed_commit is not None: if repository.installed_commit is not None:
inst_ver = "<p><b>Installed commit:</b> {}</p>".format(repository.installed_commit) inst_ver = "<p><b>Installed commit:</b> {}</p>".format(
repository.installed_commit
)
else: else:
inst_ver = "" inst_ver = ""
if repository.last_release_tag is not None: if repository.last_release_tag is not None:
last_ver = "<p><b>Available version:</b> {}</p>".format(repository.last_release_tag) last_ver = "<p><b>Available version:</b> {}</p>".format(
repository.last_release_tag
)
else: else:
last_ver = "<p><b>Available commit:</b> {}</p>".format(repository.last_commit) last_ver = "<p><b>Available commit:</b> {}</p>".format(
repository.last_commit
)
last_up = "" last_up = ""
if repository.pending_update and repository.version_installed is not None: if repository.pending_update and repository.version_installed is not None:
changelog = "<a href='https://github.com/{}/releases' target='_blank' style='color: var(--primary-color) !important'>CHANGELOG</a>".format(repository.repository_name) changelog = "<a href='https://github.com/{}/releases' target='_blank' style='color: var(--primary-color) !important'>CHANGELOG</a>".format(
repository.repository_name
)
else: else:
changelog = "" changelog = ""
if repository.installed: if repository.installed:
uninstall = "<a href='{}/repository_uninstall/{}' style='float: right; color: var(--google-red-500) !important; font-weight: bold;' onclick='ShowProgressBar()'>UNINSTALL</a>".format(self.url_path['api'], repository.repository_id) uninstall = "<a href='{}/repository_uninstall/{}' style='float: right; color: var(--google-red-500) !important; font-weight: bold;' onclick='ShowProgressBar()'>UNINSTALL</a>".format(
self.url_path["api"], repository.repository_id
)
else: else:
uninstall = "" uninstall = ""
@ -258,9 +282,30 @@ class HacsRepositoryView(HacsViewBase):
</div> </div>
</div> </div>
</div> </div>
""".format(custom_message, pending_restart, repository.name, self.url_path["api"], repository.repository_id, hide_option, repository.repository_name, """.format(
repository.name, repository.description, inst_ver, last_ver, last_up, info, authors, note, self.url_path["api"], custom_message,
repository.repository_id, main_action, changelog, repository.repository_name, open_plugin, uninstall) pending_restart,
repository.name,
self.url_path["api"],
repository.repository_id,
hide_option,
repository.repository_name,
repository.name,
repository.description,
inst_ver,
last_ver,
last_up,
info,
authors,
note,
self.url_path["api"],
repository.repository_id,
main_action,
changelog,
repository.repository_name,
open_plugin,
uninstall,
)
except Exception as exception: except Exception as exception:
_LOGGER.error(exception) _LOGGER.error(exception)

View File

@ -7,7 +7,7 @@ from homeassistant.const import __version__ as HAVERSION
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
from ...const import ISSUE_URL, NAME_LONG from ...const import ISSUE_URL, NAME_LONG
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
class HacsSettingsView(HacsViewBase): class HacsSettingsView(HacsViewBase):
@ -74,7 +74,12 @@ class HacsSettingsView(HacsViewBase):
</div> </div>
</div> </div>
</div> </div>
""".format(hacs.version_installed, hacs.last_release_tag, self.url_path["api"], hacs.last_release_tag) """.format(
hacs.version_installed,
hacs.last_release_tag,
self.url_path["api"],
hacs.last_release_tag,
)
else: else:
hacs_update = "" hacs_update = ""
@ -93,11 +98,12 @@ class HacsSettingsView(HacsViewBase):
</div> </div>
</div> </div>
</div> </div>
""".format(message) """.format(
message
)
else: else:
custom_message = "" custom_message = ""
# Repos: # Repos:
for repository in self.repositories_list_repo: for repository in self.repositories_list_repo:
if repository.hide and repository.repository_id != "172733314": if repository.hide and repository.repository_id != "172733314":
@ -107,7 +113,12 @@ class HacsSettingsView(HacsViewBase):
<i title="Unhide" class="fas fa-plus-circle" style="padding-right: 8px"></i></a> <i title="Unhide" class="fas fa-plus-circle" style="padding-right: 8px"></i></a>
{} {}
<span class="repository-list-badge">{}</span> <span class="repository-list-badge">{}</span>
""".format(self.url_path["api"], repository.repository_id, repository.repository_name, repository.repository_type) """.format(
self.url_path["api"],
repository.repository_id,
repository.repository_name,
repository.repository_type,
)
line += "</div></li>" line += "</div></li>"
hidden.append(line) hidden.append(line)
@ -117,22 +128,30 @@ class HacsSettingsView(HacsViewBase):
line = '<li class="collection-item hacscolor hacslist"><div>' line = '<li class="collection-item hacscolor hacslist"><div>'
line += """ line += """
<a href="{}/{}"><span class="repository-list-badge">{}</span> {}</a> <a href="{}/{}"><span class="repository-list-badge">{}</span> {}</a>
""".format(self.url_path["repository"], repository.repository_id, repository.repository_type, repository.repository_name) """.format(
self.url_path["repository"],
repository.repository_id,
repository.repository_type,
repository.repository_name,
)
if repository.installed: if repository.installed:
remove = """ remove = """
<i title="Remove is not possible when {} is installed." class="secondary-content fas fa-trash-alt disabledaction"></i> <i title="Remove is not possible when {} is installed." class="secondary-content fas fa-trash-alt disabledaction"></i>
""".format(repository.repository_type) """.format(
repository.repository_type
)
else: else:
remove = """ remove = """
<a href={}/repository_remove/{} onclick="ShowProgressBar()" class="secondary-content" style="color: var(--primary-color)"> <a href={}/repository_remove/{} onclick="ShowProgressBar()" class="secondary-content" style="color: var(--primary-color)">
<i title="Remove." class="fas fa-trash-alt"></i> <i title="Remove." class="fas fa-trash-alt"></i>
</a> </a>
""".format(self.url_path["api"], repository.repository_id) """.format(
self.url_path["api"], repository.repository_id
)
line += remove line += remove
line += "</div></li>" line += "</div></li>"
repository_lines.append(line) repository_lines.append(line)
# Generate content to display # Generate content to display
@ -143,7 +162,9 @@ class HacsSettingsView(HacsViewBase):
{} {}
{} {}
</div> </div>
""".format(hacs_restart, hacs_update, custom_message) """.format(
hacs_restart, hacs_update, custom_message
)
# HACS card # HACS card
content += """ content += """
@ -155,7 +176,12 @@ class HacsSettingsView(HacsViewBase):
<b>Home Assistant version:</b> {}</br> <b>Home Assistant version:</b> {}</br>
</div> </div>
</div> </div>
""".format(NAME_LONG, hacs.version_installed, " <b>(RESTART PENDING!)</b>" if hacs.pending_restart else "", HAVERSION) """.format(
NAME_LONG,
hacs.version_installed,
" <b>(RESTART PENDING!)</b>" if hacs.pending_restart else "",
HAVERSION,
)
# The buttons, must have buttons # The buttons, must have buttons
content += """ content += """
@ -173,7 +199,9 @@ class HacsSettingsView(HacsViewBase):
OPEN LOG OPEN LOG
</a> </a>
</div> </div>
""".format(self.url_path["api"], ISSUE_URL, self.url_path["api"]) """.format(
self.url_path["api"], ISSUE_URL, self.url_path["api"]
)
## Integration URL's ## Integration URL's
content += """ content += """
@ -205,7 +233,9 @@ class HacsSettingsView(HacsViewBase):
</form> </form>
</div> </div>
</div> </div>
""".format(self.url_path["api"]) """.format(
self.url_path["api"]
)
## Hidden repositories ## Hidden repositories
if hidden: if hidden:

View File

@ -6,7 +6,7 @@ from aiohttp import web
from aiohttp.web import HTTPNotFound from aiohttp.web import HTTPNotFound
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
class HacsStaticView(HacsViewBase): class HacsStaticView(HacsViewBase):
@ -21,10 +21,11 @@ class HacsStaticView(HacsViewBase):
async def get(self, request, requested_file): # pylint: disable=unused-argument async def get(self, request, requested_file): # pylint: disable=unused-argument
"""Serve static files.""" """Serve static files."""
servefile = "{}/custom_components/hacs/frontend/elements/{}".format( servefile = "{}/custom_components/hacs/frontend/elements/{}".format(
self.config_dir, requested_file) self.config_dir, requested_file
)
if os.path.exists(servefile + '.gz'): if os.path.exists(servefile + ".gz"):
return web.FileResponse(servefile + '.gz') return web.FileResponse(servefile + ".gz")
else: else:
if os.path.exists(servefile): if os.path.exists(servefile):
return web.FileResponse(servefile) return web.FileResponse(servefile)

View File

@ -4,7 +4,7 @@ import logging
from aiohttp import web from aiohttp import web
from ...blueprints import HacsViewBase from ...blueprints import HacsViewBase
_LOGGER = logging.getLogger('custom_components.hacs.frontend') _LOGGER = logging.getLogger("custom_components.hacs.frontend")
class HacsStoreView(HacsViewBase): class HacsStoreView(HacsViewBase):
@ -41,10 +41,14 @@ class HacsStoreView(HacsViewBase):
continue continue
if repository.pending_restart: if repository.pending_restart:
card_icon = "<i class='fas fa-cube card-status pending-restart'></i>" card_icon = (
"<i class='fas fa-cube card-status pending-restart'></i>"
)
elif repository.pending_update: elif repository.pending_update:
card_icon = "<i class='fas fa-cube card-status pending-update'></i>" card_icon = (
"<i class='fas fa-cube card-status pending-update'></i>"
)
elif repository.installed: elif repository.installed:
card_icon = "<i class='fas fa-cube card-status installed'></i>" card_icon = "<i class='fas fa-cube card-status installed'></i>"
@ -63,7 +67,15 @@ class HacsStoreView(HacsViewBase):
</span> </span>
</div> </div>
</a> </a>
""".format(self.url_path["repository"], repository.repository_id, repository.topics, repository.authors, card_icon, repository.name, repository.description) """.format(
self.url_path["repository"],
repository.repository_id,
repository.topics,
repository.authors,
card_icon,
repository.name,
repository.description,
)
if repository.repository_type == "integration": if repository.repository_type == "integration":
integrations.append(card) integrations.append(card)

View File

@ -8,11 +8,12 @@ from homeassistant.helpers.event import async_track_time_interval
from .aiogithub import AIOGitHubException from .aiogithub import AIOGitHubException
from .const import DEFAULT_REPOSITORIES from .const import DEFAULT_REPOSITORIES
_LOGGER = logging.getLogger('custom_components.hacs.hacs') _LOGGER = logging.getLogger("custom_components.hacs.hacs")
class HacsBase: class HacsBase:
"""The base class of HACS, nested thoughout the project.""" """The base class of HACS, nested thoughout the project."""
const = None const = None
migration = None migration = None
storage = None storage = None
@ -26,12 +27,23 @@ class HacsBase:
repositories = {} repositories = {}
url_path = {} url_path = {}
for endpoint in ["api", "error", "overview", "static", "store", "settings", "repository"]: for endpoint in [
url_path[endpoint] = "/community_{}-{}".format(str(uuid.uuid4()), str(uuid.uuid4())) "api",
"error",
"overview",
"static",
"store",
"settings",
"repository",
]:
url_path[endpoint] = "/community_{}-{}".format(
str(uuid.uuid4()), str(uuid.uuid4())
)
async def startup_tasks(self): async def startup_tasks(self):
"""Run startup_tasks.""" """Run startup_tasks."""
from .hacsrepositoryintegration import HacsRepositoryIntegration from .hacsrepositoryintegration import HacsRepositoryIntegration
self.data["task_running"] = True self.data["task_running"] = True
_LOGGER.info("Runing startup tasks.") _LOGGER.info("Runing startup tasks.")
@ -40,10 +52,14 @@ class HacsBase:
self.data["hacs"]["endpoints"] = self.url_path self.data["hacs"]["endpoints"] = self.url_path
# For installed repositories only. # For installed repositories only.
async_track_time_interval(self.hass, self.recuring_tasks_installed, timedelta(minutes=30)) async_track_time_interval(
self.hass, self.recuring_tasks_installed, timedelta(minutes=30)
)
# For the rest. # For the rest.
async_track_time_interval(self.hass, self.update_repositories, timedelta(minutes=500)) async_track_time_interval(
self.hass, self.update_repositories, timedelta(minutes=500)
)
# Check for updates to HACS. # Check for updates to HACS.
repository = await self.aiogithub.get_repo("custom-components/hacs") repository = await self.aiogithub.get_repo("custom-components/hacs")
@ -109,12 +125,17 @@ class HacsBase:
for repository in self.repositories: for repository in self.repositories:
try: try:
repository = self.repositories[repository] repository = self.repositories[repository]
if not repository.track or repository.repository_name in self.blacklist: if (
not repository.track
or repository.repository_name in self.blacklist
):
continue continue
if repository.hide and repository.repository_id != "172733314": if repository.hide and repository.repository_id != "172733314":
continue continue
if now is not None: if now is not None:
_LOGGER.info("Running update for %s", repository.repository_name) _LOGGER.info(
"Running update for %s", repository.repository_name
)
await repository.update() await repository.update()
except AIOGitHubException as exception: except AIOGitHubException as exception:
_LOGGER.error("%s - %s", repository.repository_name, exception) _LOGGER.error("%s - %s", repository.repository_name, exception)
@ -135,18 +156,22 @@ class HacsBase:
await repository.update() await repository.update()
else: else:
try: try:
await self.register_new_repository(repository_type, repository.full_name, repository) await self.register_new_repository(
repository_type, repository.full_name, repository
)
except AIOGitHubException as exception: except AIOGitHubException as exception:
_LOGGER.error("%s - %s", repository.full_name, exception) _LOGGER.error("%s - %s", repository.full_name, exception)
await self.storage.set()
self.data["task_running"] = False self.data["task_running"] = False
await self.storage.set()
async def get_repositories(self): async def get_repositories(self):
"""Get defined repositories.""" """Get defined repositories."""
repositories = {} repositories = {}
# Get org repositories # Get org repositories
repositories["integration"] = await self.aiogithub.get_org_repos("custom-components") repositories["integration"] = await self.aiogithub.get_org_repos(
"custom-components"
)
repositories["plugin"] = await self.aiogithub.get_org_repos("custom-cards") repositories["plugin"] = await self.aiogithub.get_org_repos("custom-cards")
# Additional repositories (Not implemented) # Additional repositories (Not implemented)
@ -157,7 +182,9 @@ class HacsBase:
return repositories["integration"], repositories["plugin"] return repositories["integration"], repositories["plugin"]
async def recuring_tasks_installed(self, notarealarg): # pylint: disable=unused-argument async def recuring_tasks_installed(
self, notarealarg
): # pylint: disable=unused-argument
"""Recuring tasks for installed repositories.""" """Recuring tasks for installed repositories."""
self.data["task_running"] = True self.data["task_running"] = True
_LOGGER.info("Running scheduled update of installed repositories") _LOGGER.info("Running scheduled update of installed repositories")

View File

@ -5,7 +5,7 @@ from shutil import copy2
from .hacsbase import HacsBase from .hacsbase import HacsBase
from .const import STORAGE_VERSION from .const import STORAGE_VERSION
_LOGGER = logging.getLogger('custom_components.hacs.migration') _LOGGER = logging.getLogger("custom_components.hacs.migration")
class HacsMigration(HacsBase): class HacsMigration(HacsBase):
@ -51,7 +51,9 @@ class HacsMigration(HacsBase):
if repodata.get("isinstalled"): if repodata.get("isinstalled"):
# Register new repository # Register new repository
_LOGGER.info("Migrating %s", repodata["repo"]) _LOGGER.info("Migrating %s", repodata["repo"])
repository, setup_result = await self.register_new_repository(repodata["element_type"], repodata["repo"]) repository, setup_result = await self.register_new_repository(
repodata["element_type"], repodata["repo"]
)
repository.version_installed = repodata["installed_version"] repository.version_installed = repodata["installed_version"]
repository.installed = True repository.installed = True

View File

@ -1,5 +1,5 @@
"""Blueprint for HacsRepositoryBase.""" """Blueprint for HacsRepositoryBase."""
# pylint: disable=too-many-instance-attributes,invalid-name,broad-except,wildcard-import # pylint: disable=too-many-instance-attributes,invalid-name,broad-except,wildcard-import,no-member
from asyncio import sleep from asyncio import sleep
from datetime import datetime from datetime import datetime
import logging import logging
@ -9,11 +9,16 @@ import shutil
from .aiogithub import AIOGitHubException from .aiogithub import AIOGitHubException
from .hacsbase import HacsBase from .hacsbase import HacsBase
from .exceptions import HacsRepositoryInfo, HacsUserScrewupException, HacsBaseException, HacsBlacklistException from .exceptions import (
HacsRepositoryInfo,
HacsUserScrewupException,
HacsBaseException,
HacsBlacklistException,
)
from .handler.download import async_download_file, async_save_file from .handler.download import async_download_file, async_save_file
from .const import DEFAULT_REPOSITORIES, VERSION from .const import DEFAULT_REPOSITORIES, VERSION
_LOGGER = logging.getLogger('custom_components.hacs.repository') _LOGGER = logging.getLogger("custom_components.hacs.repository")
class HacsRepositoryBase(HacsBase): class HacsRepositoryBase(HacsBase):
@ -70,10 +75,12 @@ class HacsRepositoryBase(HacsBase):
"""Return local path.""" """Return local path."""
local_path = None local_path = None
if self.repository_type == "integration": if self.repository_type == "integration":
if self.domain is None: # pylint: disable=no-member if self.domain is None:
local_path = None local_path = None
else: else:
local_path = "{}/custom_components/{}".format(self.config_dir, self.domain) # pylint: disable=no-member local_path = "{}/custom_components/{}".format(
self.config_dir, self.domain
)
elif self.repository_type == "plugin": elif self.repository_type == "plugin":
local_path = "{}/www/community/{}".format(self.config_dir, self.name) local_path = "{}/www/community/{}".format(self.config_dir, self.name)
@ -87,7 +94,9 @@ class HacsRepositoryBase(HacsBase):
@property @property
def description(self): def description(self):
"""Description.""" """Description."""
return "" if self.repository.description is None else self.repository.description return (
"" if self.repository.description is None else self.repository.description
)
@property @property
def ref(self): def ref(self):
@ -156,35 +165,53 @@ class HacsRepositoryBase(HacsBase):
except AIOGitHubException: except AIOGitHubException:
pass pass
async def download_repository_directory_content(self, repository_directory_path, local_directory, ref): async def download_repository_directory_content(
self, repository_directory_path, local_directory, ref
):
"""Download the content of a directory.""" """Download the content of a directory."""
try: try:
# Get content # Get content
if self.content_path == "release": if self.content_path == "release":
contents = self.content_objects contents = self.content_objects
else: else:
contents = await self.repository.get_contents(repository_directory_path, ref) contents = await self.repository.get_contents(
repository_directory_path, ref
)
for content_object in contents: for content_object in contents:
if content_object.type == "dir": if content_object.type == "dir":
await self.download_repository_directory_content(content_object.path, local_directory, ref) await self.download_repository_directory_content(
content_object.path, local_directory, ref
)
continue continue
if self.repository_type == "plugin" and not content_object.name.endswith(".js"): if (
self.repository_type == "plugin"
and not content_object.name.endswith(".js")
):
# For plugins we currently only need .js files # For plugins we currently only need .js files
continue continue
_LOGGER.debug("Downloading %s", content_object.name) _LOGGER.debug("Downloading %s", content_object.name)
filecontent = await async_download_file(self.hass, content_object.download_url) filecontent = await async_download_file(
self.hass, content_object.download_url
)
if filecontent is None: if filecontent is None:
_LOGGER.debug("There was an error downloading the file %s", content_object.name) _LOGGER.debug(
"There was an error downloading the file %s",
content_object.name,
)
continue continue
# Save the content of the file. # Save the content of the file.
if self.repository_name == "custom-components/hacs": if self.repository_name == "custom-components/hacs":
local_directory = "{}/{}".format(self.config_dir, content_object.path) local_directory = "{}/{}".format(
local_directory = local_directory.split("/{}".format(content_object.name))[0] self.config_dir, content_object.path
)
local_directory = local_directory.split(
"/{}".format(content_object.name)
)[0]
_LOGGER.debug(content_object.path) _LOGGER.debug(content_object.path)
_LOGGER.debug(local_directory) _LOGGER.debug(local_directory)
@ -200,7 +227,7 @@ class HacsRepositoryBase(HacsBase):
async def install(self): async def install(self):
"""Run install tasks.""" """Run install tasks."""
start_time = datetime.now() start_time = datetime.now()
_LOGGER.info('(%s) - Starting installation', self.repository_name) _LOGGER.info("(%s) - Starting installation", self.repository_name)
try: try:
# Run update # Run update
await self.update() # pylint: disable=no-member await self.update() # pylint: disable=no-member
@ -209,7 +236,9 @@ class HacsRepositoryBase(HacsBase):
await self.check_local_directory() await self.check_local_directory()
# Download files # Download files
await self.download_repository_directory_content(self.content_path, self.local_path, self.ref) await self.download_repository_directory_content(
self.content_path, self.local_path, self.ref
)
except HacsBaseException as exception: except HacsBaseException as exception:
_LOGGER.debug("(%s) - %s", self.repository_name, exception) _LOGGER.debug("(%s) - %s", self.repository_name, exception)
@ -221,8 +250,11 @@ class HacsRepositoryBase(HacsBase):
self.installed_commit = self.last_commit self.installed_commit = self.last_commit
if self.repository_type == "integration": if self.repository_type == "integration":
self.pending_restart = True self.pending_restart = True
_LOGGER.info('(%s) - installation completed in %s seconds', self.repository_name, (datetime.now() - start_time).seconds) _LOGGER.info(
"(%s) - installation completed in %s seconds",
self.repository_name,
(datetime.now() - start_time).seconds,
)
async def remove(self): async def remove(self):
"""Run remove tasks.""" """Run remove tasks."""
@ -260,21 +292,33 @@ class HacsRepositoryBase(HacsBase):
pathlib.Path(local_path).mkdir(parents=True, exist_ok=True) pathlib.Path(local_path).mkdir(parents=True, exist_ok=True)
except Exception as exception: except Exception as exception:
_LOGGER.debug("(%s) - Creating directory %s failed with %s", self.repository_name, local_path, exception) _LOGGER.debug(
"(%s) - Creating directory %s failed with %s",
self.repository_name,
local_path,
exception,
)
return return
async def remove_local_directory(self): async def remove_local_directory(self):
"""Check the local directory.""" """Check the local directory."""
try: try:
if os.path.exists(self.local_path): if os.path.exists(self.local_path):
_LOGGER.debug("(%s) - Removing %s", self.repository_name, self.local_path) _LOGGER.debug(
"(%s) - Removing %s", self.repository_name, self.local_path
)
shutil.rmtree(self.local_path) shutil.rmtree(self.local_path)
while os.path.exists(self.local_path): while os.path.exists(self.local_path):
await sleep(1) await sleep(1)
except Exception as exception: except Exception as exception:
_LOGGER.debug("(%s) - Removing directory %s failed with %s", self.repository_name, self.local_path, exception) _LOGGER.debug(
"(%s) - Removing directory %s failed with %s",
self.repository_name,
self.local_path,
exception,
)
return return
async def set_additional_info(self): async def set_additional_info(self):
@ -293,12 +337,10 @@ class HacsRepositoryBase(HacsBase):
# We kinda expect this one to fail # We kinda expect this one to fail
self.additional_info = "" self.additional_info = ""
async def set_repository(self): async def set_repository(self):
"""Set the AIOGitHub repository object.""" """Set the AIOGitHub repository object."""
self.repository = await self.aiogithub.get_repo(self.repository_name) self.repository = await self.aiogithub.get_repo(self.repository_name)
async def set_repository_releases(self): async def set_repository_releases(self):
"""Set attributes for releases.""" """Set attributes for releases."""
if self.repository is None: if self.repository is None:
@ -313,19 +355,19 @@ class HacsRepositoryBase(HacsBase):
self.last_release_object = temp self.last_release_object = temp
self.last_release_tag = temp.tag_name self.last_release_tag = temp.tag_name
async def validate_repository_name(self): async def validate_repository_name(self):
"""Validate the given repository_name.""" """Validate the given repository_name."""
if "/" not in self.repository_name: if "/" not in self.repository_name:
raise HacsUserScrewupException( raise HacsUserScrewupException(
"GitHub repository name " "GitHub repository name "
"'{}' is not the correct format".format(self.repository_name)) "'{}' is not the correct format".format(self.repository_name)
)
elif len(self.repository_name.split('/')) > 2: elif len(self.repository_name.split("/")) > 2:
raise HacsUserScrewupException( raise HacsUserScrewupException(
"GitHub repository name " "GitHub repository name "
"'{}' is not the correct format".format(self.repository_name)) "'{}' is not the correct format".format(self.repository_name)
)
async def return_last_update(self): async def return_last_update(self):
"""Return a last update string.""" """Return a last update string."""

View File

@ -6,7 +6,7 @@ from .aiogithub import AIOGitHubException
from .blueprints import HacsRepositoryBase from .blueprints import HacsRepositoryBase
from .exceptions import HacsRequirement from .exceptions import HacsRequirement
_LOGGER = logging.getLogger('custom_components.hacs.repository') _LOGGER = logging.getLogger("custom_components.hacs.repository")
class HacsRepositoryPlugin(HacsRepositoryBase): class HacsRepositoryPlugin(HacsRepositoryBase):
@ -60,7 +60,6 @@ class HacsRepositoryPlugin(HacsRepositoryBase):
pass pass
await self.set_repository_content() await self.set_repository_content()
async def set_repository_content(self): async def set_repository_content(self):
"""Set repository content attributes.""" """Set repository content attributes."""
if self.content_path is None or self.content_path == "": if self.content_path is None or self.content_path == "":

View File

@ -6,7 +6,7 @@ import json
from .blueprints import HacsRepositoryBase from .blueprints import HacsRepositoryBase
from .exceptions import HacsRequirement from .exceptions import HacsRequirement
_LOGGER = logging.getLogger('custom_components.hacs.repository') _LOGGER = logging.getLogger("custom_components.hacs.repository")
class HacsRepositoryIntegration(HacsRepositoryBase): class HacsRepositoryIntegration(HacsRepositoryBase):
@ -43,7 +43,8 @@ class HacsRepositoryIntegration(HacsRepositoryBase):
self.content_path = first[0].path self.content_path = first[0].path
self.content_objects = await self.repository.get_contents( self.content_objects = await self.repository.get_contents(
self.content_path, self.ref) self.content_path, self.ref
)
if not isinstance(self.content_objects, list): if not isinstance(self.content_objects, list):
raise HacsRequirement("Repository structure does not meet the requirements") raise HacsRequirement("Repository structure does not meet the requirements")

View File

@ -8,7 +8,7 @@ from .hacsbase import HacsBase
from .exceptions import HacsNotSoBasicException, HacsRequirement from .exceptions import HacsNotSoBasicException, HacsRequirement
from .const import STORENAME, GENERIC_ERROR, STORAGE_VERSION from .const import STORENAME, GENERIC_ERROR, STORAGE_VERSION
_LOGGER = logging.getLogger('custom_components.hacs.storage') _LOGGER = logging.getLogger("custom_components.hacs.storage")
class HacsStorage(HacsBase): class HacsStorage(HacsBase):
@ -16,16 +16,16 @@ class HacsStorage(HacsBase):
async def get(self): async def get(self):
"""Read HACS data to storage.""" """Read HACS data to storage."""
from .blueprints import ( from .blueprints import HacsRepositoryIntegration, HacsRepositoryPlugin
HacsRepositoryIntegration,
HacsRepositoryPlugin,)
datastore = "{}/.storage/{}".format(self.config_dir, STORENAME) datastore = "{}/.storage/{}".format(self.config_dir, STORENAME)
_LOGGER.debug("Reading from datastore %s.", datastore) _LOGGER.debug("Reading from datastore %s.", datastore)
self.data["task_running"] = True self.data["task_running"] = True
try: try:
async with aiofiles.open( async with aiofiles.open(
datastore, mode='r', encoding="utf-8", errors="ignore") as datafile: datastore, mode="r", encoding="utf-8", errors="ignore"
) as datafile:
store_data = await datafile.read() store_data = await datafile.read()
store_data = json.loads(store_data) store_data = json.loads(store_data)
datafile.close() datafile.close()
@ -45,7 +45,9 @@ class HacsStorage(HacsBase):
repository = store_data["repositories"][repository] repository = store_data["repositories"][repository]
if not repository.get("custom"): if not repository.get("custom"):
continue continue
repository, status = await self.register_new_repository(repository["repository_type"], repository["repository_name"]) repository, status = await self.register_new_repository(
repository["repository_type"], repository["repository_name"]
)
if status: if status:
await self.restore(store_data, repository) await self.restore(store_data, repository)
@ -65,9 +67,13 @@ class HacsStorage(HacsBase):
else: else:
_LOGGER.info("Loading %s", repository.full_name) _LOGGER.info("Loading %s", repository.full_name)
if repository_type == "integration": if repository_type == "integration":
repository = HacsRepositoryIntegration(repository.full_name, repository) repository = HacsRepositoryIntegration(
repository.full_name, repository
)
elif repository_type == "plugin": elif repository_type == "plugin":
repository = HacsRepositoryPlugin(repository.full_name, repository) repository = HacsRepositoryPlugin(
repository.full_name, repository
)
else: else:
raise HacsNotSoBasicException(GENERIC_ERROR) raise HacsNotSoBasicException(GENERIC_ERROR)
@ -76,7 +82,9 @@ class HacsStorage(HacsBase):
await repository.setup_repository() await repository.setup_repository()
except (HacsRequirement, AIOGitHubException) as exception: except (HacsRequirement, AIOGitHubException) as exception:
if not self.data["task_running"]: if not self.data["task_running"]:
_LOGGER.error("%s - %s", repository.repository_name, exception) _LOGGER.error(
"%s - %s", repository.repository_name, exception
)
self.blacklist.append(repository.repository_name) self.blacklist.append(repository.repository_name)
continue continue
@ -90,7 +98,6 @@ class HacsStorage(HacsBase):
self.data["task_running"] = False self.data["task_running"] = False
return store_data return store_data
async def set(self): async def set(self):
"""Write HACS data to storage.""" """Write HACS data to storage."""
if self.data["task_running"]: if self.data["task_running"]:
@ -123,7 +130,8 @@ class HacsStorage(HacsBase):
try: try:
async with aiofiles.open( async with aiofiles.open(
datastore, mode='w', encoding="utf-8", errors="ignore") as outfile: datastore, mode="w", encoding="utf-8", errors="ignore"
) as outfile:
await outfile.write(json.dumps(data, indent=4)) await outfile.write(json.dumps(data, indent=4))
outfile.close() outfile.close()

View File

@ -5,6 +5,7 @@ from .hacsbase import HacsBase
class HacsViewBase(HomeAssistantView, HacsBase): class HacsViewBase(HomeAssistantView, HacsBase):
"""Base View Class for HACS.""" """Base View Class for HACS."""
requires_auth = False requires_auth = False
@property @property
@ -18,7 +19,9 @@ class HacsViewBase(HomeAssistantView, HacsBase):
{} {}
<div id="main" class="hacs-content"> <div id="main" class="hacs-content">
{} {}
""".format(self.imports, self.header, self.progress_bar) """.format(
self.imports, self.header, self.progress_bar
)
@property @property
def imports(self): def imports(self):
@ -29,7 +32,9 @@ class HacsViewBase(HomeAssistantView, HacsBase):
<script src="{static}/materialize.min.js.gz"></script> <script src="{static}/materialize.min.js.gz"></script>
<link rel="stylesheet" href="{static}/hacs.css"> <link rel="stylesheet" href="{static}/hacs.css">
<script src="{static}/hacs.js"></script> <script src="{static}/hacs.js"></script>
""".format(static=self.url_path["static"]) """.format(
static=self.url_path["static"]
)
@property @property
def header(self): def header(self):
@ -46,7 +51,9 @@ class HacsViewBase(HomeAssistantView, HacsBase):
</div> </div>
</nav> </nav>
</div> </div>
""".format(self.url_path["overview"], self.url_path["store"], self.url_path["settings"]) """.format(
self.url_path["overview"], self.url_path["store"], self.url_path["settings"]
)
@property @property
def progress_bar(self): def progress_bar(self):
@ -61,7 +68,9 @@ class HacsViewBase(HomeAssistantView, HacsBase):
<div class="progress hacs-bar-background" id="progressbar" style="display: {}"> <div class="progress hacs-bar-background" id="progressbar" style="display: {}">
<div class="indeterminate hacs-bar"></div> <div class="indeterminate hacs-bar"></div>
</div> </div>
""".format(display, display) """.format(
display, display
)
@property @property
def footer(self): def footer(self):

View File

@ -10,7 +10,7 @@ import backoff
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from ..exceptions import HacsNotSoBasicException from ..exceptions import HacsNotSoBasicException
_LOGGER = logging.getLogger('custom_components.hacs.download') _LOGGER = logging.getLogger("custom_components.hacs.download")
@backoff.on_exception(backoff.expo, Exception, max_tries=3) @backoff.on_exception(backoff.expo, Exception, max_tries=3)
@ -39,7 +39,11 @@ async def async_download_file(hass, url):
else: else:
result = await request.text() result = await request.text()
else: else:
raise HacsNotSoBasicException("Got status code {} when trying to download {}".format(request.status, url)) raise HacsNotSoBasicException(
"Got status code {} when trying to download {}".format(
request.status, url
)
)
return result return result
@ -48,23 +52,25 @@ async def async_save_file(location, content):
"""Save files.""" """Save files."""
if "-bundle" in location: if "-bundle" in location:
location = location.replace("-bundle", "") location = location.replace("-bundle", "")
if "lovelace-" in location.split('/')[-1]: if "lovelace-" in location.split("/")[-1]:
search = location.split('/')[-1] search = location.split("/")[-1]
replace = search.replace("lovelace-", "") replace = search.replace("lovelace-", "")
location = location.replace(search, replace) location = location.replace(search, replace)
_LOGGER.debug("Saving %s", location) _LOGGER.debug("Saving %s", location)
mode = 'w' mode = "w"
encoding = "utf-8" encoding = "utf-8"
errors="ignore" errors = "ignore"
if not isinstance(content, str): if not isinstance(content, str):
mode = 'wb' mode = "wb"
encoding = None encoding = None
errors = None errors = None
try: try:
async with aiofiles.open(location, mode=mode, encoding=encoding, errors=errors) as outfile: async with aiofiles.open(
location, mode=mode, encoding=encoding, errors=errors
) as outfile:
await outfile.write(content) await outfile.write(content)
outfile.close() outfile.close()
@ -73,7 +79,7 @@ async def async_save_file(location, content):
_LOGGER.debug(msg) _LOGGER.debug(msg)
# Create gz for .js files # Create gz for .js files
if location.endswith('.js') or location.endswith('.css'): if location.endswith(".js") or location.endswith(".css"):
with open(location, 'rb') as f_in: with open(location, "rb") as f_in:
with gzip.open(location + '.gz', 'wb') as f_out: with gzip.open(location + ".gz", "wb") as f_out:
shutil.copyfileobj(f_in, f_out) shutil.copyfileobj(f_in, f_out)

View File

@ -5,7 +5,7 @@ import aiofiles
from ..const import STARTUP from ..const import STARTUP
_LOGGER = logging.getLogger('custom_components.hacs.log') _LOGGER = logging.getLogger("custom_components.hacs.log")
async def get_log_file_content(config_dir): async def get_log_file_content(config_dir):
@ -16,7 +16,8 @@ async def get_log_file_content(config_dir):
try: try:
async with aiofiles.open( async with aiofiles.open(
log_file, mode='r', encoding="utf-8", errors="ignore") as localfile: log_file, mode="r", encoding="utf-8", errors="ignore"
) as localfile:
logfile = await localfile.readlines() logfile = await localfile.readlines()
localfile.close() localfile.close()
for line in logfile: for line in logfile:
@ -27,7 +28,9 @@ async def get_log_file_content(config_dir):
line = line.replace(" WARNING ", "") line = line.replace(" WARNING ", "")
line = line.replace(" ERROR ", "") line = line.replace(" ERROR ", "")
line = line.replace(" CRITICAL ", "") line = line.replace(" CRITICAL ", "")
interesting += "<pre style='margin: 0; white-space: pre-wrap'>{}</pre>".format(line) interesting += "<pre style='margin: 0; white-space: pre-wrap'>{}</pre>".format(
line
)
except Exception as exception: except Exception as exception:
_LOGGER.error(exception) _LOGGER.error(exception)
return interesting return interesting

View File

@ -4,7 +4,8 @@ from . import hacs
async def async_setup_platform( async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): # pylint: disable=unused-argument hass, config, async_add_entities, discovery_info=None
): # pylint: disable=unused-argument
"""Setup sensor platform.""" """Setup sensor platform."""
async_add_entities([HACSSensor()]) async_add_entities([HACSSensor()])