Skip to content

Commit

Permalink
Preparation for next release (#33)
Browse files Browse the repository at this point in the history
* initial pref update

* doc changes

* Var updates

* Add readme paring for type

* progress

* time has pased

* initial datamigration

* Probably enough for today

* initial move to hacs

* Make it partally usable again

* It's pretty much unusable now

* it's back

* Add HacsUserException

* exception clean

* simplify

* Finish up HacsRepositoryBase

* Remove comments

* Skip skippable

* Initial work for view classes

* set 0.93 as minimum

* remove

* add custom-updater and tracker-card to blacklist

* Started HacsRepositoryIntegration

* Implement a static view

* current progress

* More progress

* clean

* aaaand we are back to completly useless

* A little more usabe, still broken

* Keep old structure

* Getting closer

* Changes before lunsj

* Mostly all back again

* repoview

* Starting to look good

* minor update

* restore not working

* Restore partally works again

* Can remove/uninstall now

* now we have something

* Add search to store

* Fix mathcing

* fix return_last_update

* remove seconds

* order

* Initial AIOGitHub

* simple restore

* move more over to aiogithub

* releases

* Fix exception

* more stuff over to aiogithub

* Started hacs split

* more cleanup

* some exception handling

* Some bugs ahead

* Fix bug with downloading releasaes

* soooo... this is broken

* less broken

* Fix @maykar's wierd readme

* progress

* Add autofocus

* update

* Started cleanup

* More cleanup

* some change

* make sure

* that's better

* even better

* Fixes restore issue

* mostly done

* add message

* so close

* Change requirements

* This is so stupid.... py35 should be banned...
  • Loading branch information
ludeeus committed Jun 1, 2019
1 parent 16eb091 commit ac52556
Show file tree
Hide file tree
Showing 42 changed files with 2,717 additions and 2,373 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## 📘 Documentation: https://custom-components.github.io/hacs/

**YouTube Promo for HACS🙈:**

[![HACS Promo](https://img.youtube.com/vi/vq0qQtDAOW0/0.jpg)](https://www.youtube.com/watch?v=vq0qQtDAOW0 "HACS Promo")

## Issues
Expand Down
252 changes: 67 additions & 185 deletions custom_components/hacs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
import os.path
import json
import asyncio
from datetime import timedelta
from datetime import datetime, timedelta
from pkg_resources import parse_version
import aiohttp

import voluptuous as vol
from homeassistant.const import EVENT_HOMEASSISTANT_START, __version__ as HAVERSION
from homeassistant.helpers.aiohttp_client import async_create_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_time_interval

from custom_components.hacs.hacsbase import HacsBase as hacs
from custom_components.hacs.const import (
CUSTOM_UPDATER_LOCATIONS,
STARTUP,
PROJECT_URL,
ISSUE_URL,
CUSTOM_UPDATER_WARNING,
NAME_LONG,
Expand All @@ -26,34 +29,25 @@
ELEMENT_TYPES,
VERSION,
IFRAME,
SKIP,
)
from custom_components.hacs.element import Element
from custom_components.hacs.handler.storage import (
get_data_from_store,
write_to_data_store,
)
from custom_components.hacs.handler.update import (
load_integrations_from_git,
load_plugins_from_git,
)

from custom_components.hacs.frontend.views import (
CommunityOverview,
CommunityElement,
CommunityPlugin,
CommunityStore,
CommunitySettings,
CommunityAPI,
HacsStaticView,
HacsErrorView,
HacsPluginView,
HacsOverviewView,
HacsStoreView,
HacsSettingsView,
HacsRepositoryView,
HacsAPIView,
)

DOMAIN = "{}".format(NAME_SHORT.lower())

INTERVAL = timedelta(minutes=500)

# TODO: Requirements are not loaded from manifest, needs investigation.
REQUIREMENTS = ["PyGithub", "markdown"]
# TODO: Remove this when minimum HA version is > 0.93
REQUIREMENTS = ["aiofiles"]

_LOGGER = logging.getLogger(__name__)
_LOGGER = logging.getLogger('custom_components.hacs')

CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema({vol.Required("token"): cv.string})}, extra=vol.ALLOW_EXTRA
Expand All @@ -65,183 +59,71 @@ async def async_setup(hass, config): # pylint: disable=unused-argument
_LOGGER.info(STARTUP)
config_dir = hass.config.path()
github_token = config[DOMAIN]["token"]
commander = HacsCommander(hass, github_token)

# Check if custom_updater exists
# Configure HACS
await configure_hacs(hass, github_token, config_dir)

# Check if custom_updater exists
for location in CUSTOM_UPDATER_LOCATIONS:
if os.path.exists(location.format(config_dir)):
msg = CUSTOM_UPDATER_WARNING.format(location.format(config_dir))
_LOGGER.critical(msg)
return False

# Check if HA is the required version.
if int(HAVERSION.split(".")[1]) < 92:
if parse_version(HAVERSION) < parse_version('0.92.0'):
_LOGGER.critical("You need HA version 92 or newer to use this integration.")
return False

# Setup background tasks
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, commander.startup_tasks())
# Setup startup tasks
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, hacs().startup_tasks())

# Register the views
hass.http.register_view(CommunityOverview(hass))
hass.http.register_view(CommunityElement(hass))
hass.http.register_view(CommunityStore(hass))
hass.http.register_view(CommunityPlugin(hass))
hass.http.register_view(CommunitySettings(hass))
hass.http.register_view(CommunityAPI(hass))

hass.data[DOMAIN_DATA] = {}
hass.data[DOMAIN_DATA]["commander"] = commander
hass.http.register_view(HacsStaticView())
hass.http.register_view(HacsErrorView())
hass.http.register_view(HacsPluginView())
hass.http.register_view(HacsStoreView())
hass.http.register_view(HacsOverviewView())
hass.http.register_view(HacsSettingsView())
hass.http.register_view(HacsRepositoryView())
hass.http.register_view(HacsAPIView())

# Add to sidepanel
await hass.components.frontend.async_register_built_in_panel(
"iframe",
IFRAME["title"],
IFRAME["icon"],
IFRAME["path"],
{"url": IFRAME["url"]},
require_admin=IFRAME["require_admin"],
)
# TODO: Remove this check when minimum HA version is > 0.94
if parse_version(HAVERSION) < parse_version('0.94.0'):
hass.components.frontend.async_register_built_in_panel(
"iframe",
IFRAME["title"],
IFRAME["icon"],
IFRAME["path"],
{"url": hacs.url_path["overview"]},
require_admin=IFRAME["require_admin"],
)
else:
await hass.components.frontend.async_register_built_in_panel(
"iframe",
IFRAME["title"],
IFRAME["icon"],
IFRAME["path"],
{"url": hacs.url_path["overview"]},
require_admin=IFRAME["require_admin"],
)

# Mischief managed!
return True


class HacsCommander:
"""HACS Commander class."""

def __init__(self, hass, github_token):
"""Initialize HacsCommander"""
import github

self.hass = hass
self.git = github.Github(github_token)
self.skip = SKIP

async def startup_tasks(self):
"""Run startup_tasks."""
_LOGGER.debug("Runing startup_tasks.")
custom_log_level = {"custom_components.hacs": "debug"}
await self.hass.services.async_call("logger", "set_level", custom_log_level)
async_track_time_interval(self.hass, self.repetetive_tasks, INTERVAL)

_LOGGER.info("Loading existing data.")

returndata = await get_data_from_store(self.hass.config.path())
if not returndata.get("elements"):
_LOGGER.info(
"Data did not exist running initial setup, this will take some time."
)
self.hass.data[DOMAIN_DATA]["elements"] = {}
self.hass.data[DOMAIN_DATA]["repos"] = {"integration": [], "plugin": []}
self.hass.data[DOMAIN_DATA]["hacs"] = {"local": VERSION, "remote": None}
self.hass.async_create_task(self.repetetive_tasks())
else:
self.hass.data[DOMAIN_DATA]["elements"] = returndata["elements"]
self.hass.data[DOMAIN_DATA]["repos"] = returndata["repos"]
self.hass.data[DOMAIN_DATA]["hacs"] = returndata["hacs"]

# Make sure we have the correct version
self.hass.data[DOMAIN_DATA]["hacs"]["local"] = VERSION

# Reset restart_pending flag
self.hass.data[DOMAIN_DATA]["hacs"]["restart_pending"] = False
for element in self.hass.data[DOMAIN_DATA]["elements"]:
element = self.hass.data[DOMAIN_DATA]["elements"][element]
element.restart_pending = False
self.hass.data[DOMAIN_DATA]["elements"][element.element_id] = element
await write_to_data_store(self.hass.config.path(), self.hass.data[DOMAIN_DATA])

async def repetetive_tasks(self, runas=None): # pylint: disable=unused-argument
"""Run repetetive tasks."""
_LOGGER.debug("Run repetetive_tasks.")

# Check HACS
try:
hacs = self.git.get_repo("custom-components/hacs")
self.hass.data[DOMAIN_DATA]["hacs"]["remote"] = list(hacs.get_releases())[
0
].tag_name
except Exception as error: # pylint: disable=broad-except
_LOGGER.debug(error)

integration_repos = []
plugin_repos = []

# Add integration repos to check list.
## Custom repos
if self.hass.data[DOMAIN_DATA]["repos"].get("integration"):
for entry in self.hass.data[DOMAIN_DATA]["repos"].get("integration"):
_LOGGER.debug("Checking custom repo %s", entry)
repo = entry
if "http" in repo:
repo = repo.split("https://github.com/")[-1]

if len(repo.split("/")) != 2:
_LOGGER.error("%s is not valid", entry)
continue

try:
repo = self.git.get_repo(repo)
if not repo.archived or repo.full_name not in self.skip:
integration_repos.append(repo.full_name)
except Exception as error: # pylint: disable=broad-except
_LOGGER.error(error)

## Org repos
for repo in list(self.git.get_organization("custom-components").get_repos()):
if repo.archived:
continue
if repo.full_name in self.skip:
continue
integration_repos.append(repo.full_name)

# Add plugin repos to check list.
## Custom repos
if self.hass.data[DOMAIN_DATA]["repos"].get("plugin"):
for entry in self.hass.data[DOMAIN_DATA]["repos"].get("plugin"):
_LOGGER.debug("Checking custom repo %s", entry)
repo = entry
if "http" in repo:
repo = repo.split("https://github.com/")[-1]

if len(repo.split("/")) != 2:
_LOGGER.error("%s is not valid", entry)
continue

try:
repo = self.git.get_repo(repo)
if not repo.archived or repo.full_name not in self.skip:
plugin_repos.append(repo.full_name)
except Exception as error: # pylint: disable=broad-except
_LOGGER.error(error)

## Org repos
for repo in list(self.git.get_organization("custom-cards").get_repos()):
if repo.archived:
continue
if repo.full_name in self.skip:
continue
plugin_repos.append(repo.full_name)

_LOGGER.debug(integration_repos)

for repo in integration_repos:
await load_integrations_from_git(self.hass, repo)

_LOGGER.debug(plugin_repos)

self.hass.async_create_task(
self.prosess_repos(integration_repos, "integration")
)
self.hass.async_create_task(self.prosess_repos(plugin_repos, "plugin"))

async def prosess_repos(self, repos, repo_type):
"""Prosess repos."""
if repo_type == "integraion":
for repo in repos:
await load_integrations_from_git(self.hass, repo)
elif repo_type == "plugin":
for repo in repos:
await load_plugins_from_git(self.hass, repo)
await write_to_data_store(self.hass.config.path(), self.hass.data[DOMAIN_DATA])
async def configure_hacs(hass, github_token, hass_config_dir):
"""Configure HACS."""
from custom_components.hacs.aiogithub import AIOGitHub
from custom_components.hacs.hacsmigration import HacsMigration
from custom_components.hacs.hacsstorage import HacsStorage

hacs.migration = HacsMigration()
hacs.storage = HacsStorage()

hacs.aiogithub = AIOGitHub(github_token, hass.loop, async_create_clientsession(hass))

hacs.hass = hass
hacs.config_dir = hass_config_dir
hacs.blacklist = hacs.const.BLACKLIST
Loading

0 comments on commit ac52556

Please sign in to comment.