Skip to content

Commit 766a9af

Browse files
committed
Update version after merge
2 parents ba96f99 + ca303a6 commit 766a9af

26 files changed

+942
-667
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ sudo: false
22
matrix:
33
fast_finish: true
44
include:
5-
- python: "3.5"
5+
- python: "3.6"
66

77
cache:
88
directories:

API.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ Get all available addons
7878
"repositories": [
7979
{
8080
"slug": "12345678",
81-
"name": "Repitory Name",
81+
"name": "Repitory Name|unknown",
8282
"source": "URL_OF_REPOSITORY",
83-
"url": "null|WEBSITE",
84-
"maintainer": "null|BLA BLU <fla@dld.ch>"
83+
"url": "WEBSITE|REPOSITORY",
84+
"maintainer": "BLA BLU <fla@dld.ch>|unknown"
8585
}
8686
]
8787
}
@@ -239,12 +239,12 @@ Output the raw docker log
239239
"url": "null|url of addon",
240240
"detached": "bool",
241241
"repository": "12345678|null",
242-
"version": "VERSION",
242+
"version": "null|VERSION_INSTALLED",
243243
"last_version": "LAST_VERSION",
244-
"state": "started|stopped",
244+
"state": "none|started|stopped",
245245
"boot": "auto|manual",
246246
"build": "bool",
247-
"options": {},
247+
"options": "{}",
248248
}
249249
```
250250

hassio/__main__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Main file for HassIO."""
22
import asyncio
3+
from concurrent.futures import ThreadPoolExecutor
34
import logging
45
import sys
56

@@ -17,7 +18,14 @@
1718
exit(1)
1819

1920
loop = asyncio.get_event_loop()
20-
hassio = core.HassIO(loop)
21+
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
22+
loop.set_default_executor(executor)
23+
24+
_LOGGER.info("Initialize Hassio setup")
25+
config = bootstrap.initialize_system_data()
26+
hassio = core.HassIO(loop, config)
27+
28+
bootstrap.migrate_system_env(config)
2129

2230
_LOGGER.info("Run Hassio setup")
2331
loop.run_until_complete(hassio.setup())
@@ -26,7 +34,11 @@
2634
loop.call_soon_threadsafe(loop.create_task, hassio.start())
2735
loop.call_soon_threadsafe(bootstrap.reg_signal, loop, hassio)
2836

37+
_LOGGER.info("Run Hassio loop")
2938
loop.run_forever()
39+
40+
_LOGGER.info("Cleanup system")
41+
executor.shutdown(wait=False)
3042
loop.close()
3143

3244
_LOGGER.info("Close Hassio")

hassio/addons/__init__.py

Lines changed: 94 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,220 +1,133 @@
11
"""Init file for HassIO addons."""
22
import asyncio
33
import logging
4-
import shutil
54

6-
from .data import AddonsData
7-
from .git import AddonsRepoHassIO, AddonsRepoCustom
8-
from ..const import STATE_STOPPED, STATE_STARTED
9-
from ..dock.addon import DockerAddon
5+
from .addon import Addon
6+
from .repository import Repository
7+
from .data import Data
8+
from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL, BOOT_AUTO
109

1110
_LOGGER = logging.getLogger(__name__)
1211

12+
BUILTIN_REPOSITORIES = set((REPOSITORY_CORE, REPOSITORY_LOCAL))
1313

14-
class AddonManager(AddonsData):
14+
15+
class AddonManager(object):
1516
"""Manage addons inside HassIO."""
1617

1718
def __init__(self, config, loop, dock):
1819
"""Initialize docker base wrapper."""
19-
super().__init__(config)
20-
2120
self.loop = loop
21+
self.config = config
2222
self.dock = dock
23-
self.repositories = []
24-
self.dockers = {}
23+
self.data = Data(config)
24+
self.addons = {}
25+
self.repositories = {}
26+
27+
@property
28+
def list_addons(self):
29+
"""Return a list of all addons."""
30+
return list(self.addons.values())
31+
32+
@property
33+
def list_repositories(self):
34+
"""Return list of addon repositories."""
35+
return list(self.repositories.values())
36+
37+
def get(self, addon_slug):
38+
"""Return a adddon from slug."""
39+
return self.addons.get(addon_slug)
2540

26-
async def prepare(self, arch):
41+
async def prepare(self):
2742
"""Startup addon management."""
28-
self.arch = arch
43+
self.data.reload()
2944

30-
# init hassio repository
31-
self.repositories.append(AddonsRepoHassIO(self.config, self.loop))
45+
# init hassio built-in repositories
46+
repositories = \
47+
set(self.config.addons_repositories) | BUILTIN_REPOSITORIES
3248

33-
# init custom repositories
34-
for url in self.config.addons_repositories:
35-
self.repositories.append(
36-
AddonsRepoCustom(self.config, self.loop, url))
49+
# init custom repositories & load addons
50+
await self.load_repositories(repositories)
3751

38-
# load addon repository
39-
tasks = [addon.load() for addon in self.repositories]
52+
async def reload(self):
53+
"""Update addons from repo and reload list."""
54+
tasks = [repository.update() for repository in
55+
self.repositories.values()]
4056
if tasks:
4157
await asyncio.wait(tasks, loop=self.loop)
4258

43-
# read data from repositories
44-
self.read_data_from_repositories()
45-
self.merge_update_config()
59+
# read data from repositories
60+
self.data.reload()
4661

47-
# load installed addons
48-
for addon in self.list_installed:
49-
self.dockers[addon] = DockerAddon(
50-
self.config, self.loop, self.dock, self, addon)
51-
await self.dockers[addon].attach()
62+
# update addons
63+
await self.load_addons()
5264

53-
async def add_git_repository(self, url):
65+
async def load_repositories(self, list_repositories):
5466
"""Add a new custom repository."""
55-
if url in self.config.addons_repositories:
56-
_LOGGER.warning("Repository already exists %s", url)
57-
return False
67+
new_rep = set(list_repositories)
68+
old_rep = set(self.repositories)
69+
70+
# add new repository
71+
async def _add_repository(url):
72+
"""Helper function to async add repository."""
73+
repository = Repository(self.config, self.loop, self.data, url)
74+
if not await repository.load():
75+
_LOGGER.error("Can't load from repository %s", url)
76+
return
77+
self.repositories[url] = repository
78+
79+
# don't add built-in repository to config
80+
if url not in BUILTIN_REPOSITORIES:
81+
self.config.addons_repositories = url
82+
83+
tasks = [_add_repository(url) for url in new_rep - old_rep]
84+
if tasks:
85+
await asyncio.wait(tasks, loop=self.loop)
5886

59-
repo = AddonsRepoCustom(self.config, self.loop, url)
87+
# del new repository
88+
for url in old_rep - new_rep - BUILTIN_REPOSITORIES:
89+
self.repositories.pop(url).remove()
90+
self.config.drop_addon_repository(url)
6091

61-
if not await repo.load():
62-
_LOGGER.error("Can't load from repository %s", url)
63-
return False
92+
# update data
93+
self.data.reload()
94+
await self.load_addons()
6495

65-
self.config.addons_repositories = url
66-
self.repositories.append(repo)
67-
return True
96+
async def load_addons(self):
97+
"""Update/add internal addon store."""
98+
all_addons = set(self.data.system) | set(self.data.cache)
6899

69-
def drop_git_repository(self, url):
70-
"""Remove a custom repository."""
71-
for repo in self.repositories:
72-
if repo.url == url:
73-
self.repositories.remove(repo)
74-
self.config.drop_addon_repository(url)
75-
repo.remove()
76-
return True
100+
# calc diff
101+
add_addons = all_addons - set(self.addons)
102+
del_addons = set(self.addons) - all_addons
77103

78-
return False
104+
_LOGGER.info("Load addons: %d all - %d new - %d remove",
105+
len(all_addons), len(add_addons), len(del_addons))
79106

80-
async def reload(self):
81-
"""Update addons from repo and reload list."""
82-
tasks = [addon.pull() for addon in self.repositories]
83-
if not tasks:
84-
return
107+
# new addons
108+
tasks = []
109+
for addon_slug in add_addons:
110+
addon = Addon(
111+
self.config, self.loop, self.dock, self.data, addon_slug)
85112

86-
await asyncio.wait(tasks, loop=self.loop)
113+
tasks.append(addon.load())
114+
self.addons[addon_slug] = addon
87115

88-
# read data from repositories
89-
self.read_data_from_repositories()
90-
self.merge_update_config()
116+
if tasks:
117+
await asyncio.wait(tasks, loop=self.loop)
91118

92-
# remove stalled addons
93-
for addon in self.list_detached:
94-
_LOGGER.warning("Dedicated addon '%s' found!", addon)
119+
# remove
120+
for addon_slug in del_addons:
121+
self.addons.pop(addon_slug)
95122

96-
async def auto_boot(self, start_type):
123+
async def auto_boot(self, stage):
97124
"""Boot addons with mode auto."""
98-
boot_list = self.list_startup(start_type)
99-
tasks = [self.start(addon) for addon in boot_list]
125+
tasks = []
126+
for addon in self.addons.values():
127+
if addon.is_installed and addon.boot == BOOT_AUTO and \
128+
addon.startup == stage:
129+
tasks.append(addon.start())
100130

101-
_LOGGER.info("Startup %s run %d addons", start_type, len(tasks))
131+
_LOGGER.info("Startup %s run %d addons", stage, len(tasks))
102132
if tasks:
103133
await asyncio.wait(tasks, loop=self.loop)
104-
105-
async def install(self, addon, version=None):
106-
"""Install a addon."""
107-
if not self.exists_addon(addon):
108-
_LOGGER.error("Addon %s not exists for install", addon)
109-
return False
110-
111-
if self.arch not in self.get_arch(addon):
112-
_LOGGER.error("Addon %s not supported on %s", addon, self.arch)
113-
return False
114-
115-
if self.is_installed(addon):
116-
_LOGGER.error("Addon %s is already installed", addon)
117-
return False
118-
119-
if not self.path_data(addon).is_dir():
120-
_LOGGER.info("Create Home-Assistant addon data folder %s",
121-
self.path_data(addon))
122-
self.path_data(addon).mkdir()
123-
124-
addon_docker = DockerAddon(
125-
self.config, self.loop, self.dock, self, addon)
126-
127-
version = version or self.get_last_version(addon)
128-
if not await addon_docker.install(version):
129-
return False
130-
131-
self.dockers[addon] = addon_docker
132-
self.set_addon_install(addon, version)
133-
return True
134-
135-
async def uninstall(self, addon):
136-
"""Remove a addon."""
137-
if not self.is_installed(addon):
138-
_LOGGER.error("Addon %s is already uninstalled", addon)
139-
return False
140-
141-
if addon not in self.dockers:
142-
_LOGGER.error("No docker found for addon %s", addon)
143-
return False
144-
145-
if not await self.dockers[addon].remove():
146-
return False
147-
148-
if self.path_data(addon).is_dir():
149-
_LOGGER.info("Remove Home-Assistant addon data folder %s",
150-
self.path_data(addon))
151-
shutil.rmtree(str(self.path_data(addon)))
152-
153-
self.dockers.pop(addon)
154-
self.set_addon_uninstall(addon)
155-
return True
156-
157-
async def state(self, addon):
158-
"""Return running state of addon."""
159-
if addon not in self.dockers:
160-
_LOGGER.error("No docker found for addon %s", addon)
161-
return
162-
163-
if await self.dockers[addon].is_running():
164-
return STATE_STARTED
165-
return STATE_STOPPED
166-
167-
async def start(self, addon):
168-
"""Set options and start addon."""
169-
if addon not in self.dockers:
170-
_LOGGER.error("No docker found for addon %s", addon)
171-
return False
172-
173-
if not self.write_addon_options(addon):
174-
_LOGGER.error("Can't write options for addon %s", addon)
175-
return False
176-
177-
return await self.dockers[addon].run()
178-
179-
async def stop(self, addon):
180-
"""Stop addon."""
181-
if addon not in self.dockers:
182-
_LOGGER.error("No docker found for addon %s", addon)
183-
return False
184-
185-
return await self.dockers[addon].stop()
186-
187-
async def update(self, addon, version=None):
188-
"""Update addon."""
189-
if addon not in self.dockers:
190-
_LOGGER.error("No docker found for addon %s", addon)
191-
return False
192-
193-
version = version or self.get_last_version(addon)
194-
195-
# update
196-
if not await self.dockers[addon].update(version):
197-
return False
198-
199-
self.set_addon_update(addon, version)
200-
return True
201-
202-
async def restart(self, addon):
203-
"""Restart addon."""
204-
if addon not in self.dockers:
205-
_LOGGER.error("No docker found for addon %s", addon)
206-
return False
207-
208-
if not self.write_addon_options(addon):
209-
_LOGGER.error("Can't write options for addon %s", addon)
210-
return False
211-
212-
return await self.dockers[addon].restart()
213-
214-
async def logs(self, addon):
215-
"""Return addons log output."""
216-
if addon not in self.dockers:
217-
_LOGGER.error("No docker found for addon %s", addon)
218-
return False
219-
220-
return await self.dockers[addon].logs()

0 commit comments

Comments
 (0)