Skip to content

Commit 803eb0f

Browse files
authored
Merge pull request #108 from home-assistant/dev
Release 0.46
2 parents 14bf834 + 58c5ed7 commit 803eb0f

15 files changed

+121
-93
lines changed

API.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,11 @@ The addons from `addons` are only installed one.
4040
"name": "xy bla",
4141
"slug": "xy",
4242
"description": "description",
43-
"arch": ["armhf", "aarch64", "i386", "amd64"],
4443
"repository": "12345678|null",
4544
"version": "LAST_VERSION",
4645
"installed": "INSTALL_VERSION",
47-
"detached": "bool",
48-
"build": "bool",
49-
"url": "null|url"
46+
"logo": "bool",
47+
"state": "started|stopped",
5048
}
5149
],
5250
"addons_repositories": [
@@ -55,10 +53,6 @@ The addons from `addons` are only installed one.
5553
}
5654
```
5755

58-
- GET `/supervisor/addons`
59-
60-
Get all available addons. Will be delete soon. Look to `/addons`
61-
6256
- POST `/supervisor/update`
6357
Optional:
6458
```json
@@ -299,7 +293,8 @@ Get all available addons
299293
"installed": "none|INSTALL_VERSION",
300294
"detached": "bool",
301295
"build": "bool",
302-
"url": "null|url"
296+
"url": "null|url",
297+
"logo": "bool"
303298
}
304299
],
305300
"repositories": [
@@ -332,10 +327,14 @@ Get all available addons
332327
"build": "bool",
333328
"options": "{}",
334329
"network": "{}|null",
335-
"host_network": "bool"
330+
"host_network": "bool",
331+
"logo": "bool",
332+
"webui": "null|http(s)://[HOST]:port/xy/zx"
336333
}
337334
```
338335

336+
- GET `/addons/{addon}/logo`
337+
339338
- POST `/addons/{addon}/options`
340339
```json
341340
{

hassio/addons/addon.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
ATTR_URL, ATTR_ARCH, ATTR_LOCATON, ATTR_DEVICES, ATTR_ENVIRONMENT,
2020
ATTR_HOST_NETWORK, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_STARTUP,
2121
STATE_STARTED, STATE_STOPPED, STATE_NONE, ATTR_USER, ATTR_SYSTEM,
22-
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK)
22+
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_WEBUI)
2323
from .util import check_installed
2424
from ..dock.addon import DockerAddon
2525
from ..tools import write_json_file, read_json_file
2626

2727
_LOGGER = logging.getLogger(__name__)
2828

2929
RE_VOLUME = re.compile(MAP_VOLUME)
30+
RE_WEBUI = re.compile(r"^(.*\[HOST\]:)\[PORT:(\d+)\](.*)$")
3031

3132

3233
class Addon(object):
@@ -130,7 +131,8 @@ def boot(self, value):
130131
@property
131132
def auto_update(self):
132133
"""Return if auto update is enable."""
133-
return self.data.user[self._id][ATTR_AUTO_UPDATE]
134+
if ATTR_AUTO_UPDATE in self.data.user.get(self._id, {}):
135+
return self.data.user[self._id][ATTR_AUTO_UPDATE]
134136

135137
@auto_update.setter
136138
def auto_update(self, value):
@@ -196,6 +198,25 @@ def ports(self, value):
196198

197199
self.data.save()
198200

201+
@property
202+
def webui(self):
203+
"""Return URL to webui or None."""
204+
if ATTR_WEBUI not in self._mesh:
205+
return
206+
207+
webui = self._mesh[ATTR_WEBUI]
208+
dock_port = RE_WEBUI.sub(r"\2", webui)
209+
if self.ports is None:
210+
real_port = dock_port
211+
else:
212+
real_port = self.ports.get("{}/tcp".format(dock_port), dock_port)
213+
214+
# for interface config or port lists
215+
if isinstance(real_port, (tuple, list)):
216+
real_port = real_port[-1]
217+
218+
return RE_WEBUI.sub(r"\g<1>{}\g<3>".format(real_port), webui)
219+
199220
@property
200221
def network_mode(self):
201222
"""Return network mode of addon."""
@@ -228,6 +249,11 @@ def url(self):
228249
"""Return url of addon."""
229250
return self._mesh.get(ATTR_URL)
230251

252+
@property
253+
def with_logo(self):
254+
"""Return True if a logo exists."""
255+
return self.path_logo.exists()
256+
231257
@property
232258
def supported_arch(self):
233259
"""Return list of supported arch."""
@@ -273,23 +299,28 @@ def path_extern_data(self):
273299
return PurePath(self.config.path_extern_addons_data, self._id)
274300

275301
@property
276-
def path_addon_options(self):
302+
def path_options(self):
277303
"""Return path to addons options."""
278304
return Path(self.path_data, "options.json")
279305

280306
@property
281-
def path_addon_location(self):
307+
def path_location(self):
282308
"""Return path to this addon."""
283309
return Path(self._mesh[ATTR_LOCATON])
284310

311+
@property
312+
def path_logo(self):
313+
"""Return path to addon logo."""
314+
return Path(self.path_location, 'logo.png')
315+
285316
def write_options(self):
286317
"""Return True if addon options is written to data."""
287318
schema = self.schema
288319
options = self.options
289320

290321
try:
291322
schema(options)
292-
return write_json_file(self.path_addon_options, options)
323+
return write_json_file(self.path_options, options)
293324
except vol.Invalid as ex:
294325
_LOGGER.error("Addon %s have wrong options -> %s", self._id,
295326
humanize_error(options, ex))

hassio/addons/validate.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
ARCH_AARCH64, ARCH_AMD64, ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED,
1111
ATTR_USER, ATTR_STATE, ATTR_SYSTEM, STATE_STARTED, STATE_STOPPED,
1212
ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
13-
ATTR_AUTO_UPDATE)
13+
ATTR_AUTO_UPDATE, ATTR_WEBUI)
1414
from ..validate import NETWORK_PORT, DOCKER_PORTS
1515

1616

@@ -65,6 +65,8 @@ def _migrate_startup(value):
6565
vol.Required(ATTR_BOOT):
6666
vol.In([BOOT_AUTO, BOOT_MANUAL]),
6767
vol.Optional(ATTR_PORTS): DOCKER_PORTS,
68+
vol.Optional(ATTR_WEBUI):
69+
vol.Match(r"^(?:https?):\/\/\[HOST\]:\[PORT:\d+\].*$"),
6870
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
6971
vol.Optional(ATTR_DEVICES): [vol.Match(r"^(.*):(.*):([rwm]{1,3})$")],
7072
vol.Optional(ATTR_TMPFS):

hassio/api/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ def register_supervisor(self, supervisor, snapshots, addons, host_control,
5353

5454
self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping)
5555
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)
56-
self.webapp.router.add_get(
57-
'/supervisor/addons', api_supervisor.available_addons)
5856
self.webapp.router.add_post(
5957
'/supervisor/update', api_supervisor.update)
6058
self.webapp.router.add_post(
@@ -94,6 +92,7 @@ def register_addons(self, addons):
9492
self.webapp.router.add_post(
9593
'/addons/{addon}/options', api_addons.options)
9694
self.webapp.router.add_get('/addons/{addon}/logs', api_addons.logs)
95+
self.webapp.router.add_get('/addons/{addon}/logo', api_addons.logo)
9796

9897
def register_security(self):
9998
"""Register security function."""

hassio/api/addons.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY,
1212
ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK, ATTR_SLUG,
1313
ATTR_SOURCE, ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_ARCH, ATTR_MAINTAINER,
14-
ATTR_INSTALLED, BOOT_AUTO, BOOT_MANUAL)
14+
ATTR_INSTALLED, ATTR_LOGO, ATTR_WEBUI, BOOT_AUTO, BOOT_MANUAL,
15+
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY)
1516
from ..validate import DOCKER_PORTS
1617

1718
_LOGGER = logging.getLogger(__name__)
@@ -64,6 +65,7 @@ async def list(self, request):
6465
ATTR_REPOSITORY: addon.repository,
6566
ATTR_BUILD: addon.need_build,
6667
ATTR_URL: addon.url,
68+
ATTR_LOGO: addon.with_logo,
6769
})
6870

6971
data_repositories = []
@@ -82,9 +84,10 @@ async def list(self, request):
8284
}
8385

8486
@api_process
85-
def reload(self, request):
87+
async def reload(self, request):
8688
"""Reload all addons data."""
87-
return self.addons.reload()
89+
await asyncio.shield(self.addons.reload(), loop=self.loop)
90+
return True
8891

8992
@api_process
9093
async def info(self, request):
@@ -106,6 +109,8 @@ async def info(self, request):
106109
ATTR_BUILD: addon.need_build,
107110
ATTR_NETWORK: addon.ports,
108111
ATTR_HOST_NETWORK: addon.network_mode == 'host',
112+
ATTR_LOGO: addon.with_logo,
113+
ATTR_WEBUI: addon.webui,
109114
}
110115

111116
@api_process
@@ -182,8 +187,18 @@ async def restart(self, request):
182187
addon = self._extract_addon(request)
183188
return await asyncio.shield(addon.restart(), loop=self.loop)
184189

185-
@api_process_raw
190+
@api_process_raw(CONTENT_TYPE_BINARY)
186191
def logs(self, request):
187192
"""Return logs from addon."""
188193
addon = self._extract_addon(request)
189194
return addon.logs()
195+
196+
@api_process_raw(CONTENT_TYPE_PNG)
197+
async def logo(self, request):
198+
"""Return logo from addon."""
199+
addon = self._extract_addon(request, check_installed=False)
200+
if not addon.with_logo:
201+
raise RuntimeError("No image found!")
202+
203+
with addon.path_logo.open('rb') as png:
204+
return png.read()

hassio/api/homeassistant.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
from .util import api_process, api_process_raw, api_validate
88
from ..const import (
9-
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM)
9+
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM,
10+
CONTENT_TYPE_BINARY)
1011
from ..validate import HASS_DEVICES
1112

1213
_LOGGER = logging.getLogger(__name__)
@@ -79,7 +80,7 @@ async def restart(self, request):
7980
return await asyncio.shield(
8081
self.homeassistant.restart(), loop=self.loop)
8182

82-
@api_process_raw
83+
@api_process_raw(CONTENT_TYPE_BINARY)
8384
def logs(self, request):
8485
"""Return homeassistant docker logs.
8586

hassio/api/snapshots.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ async def list(self, request):
6363
}
6464

6565
@api_process
66-
def reload(self, request):
66+
async def reload(self, request):
6767
"""Reload snapshot list."""
68-
return asyncio.shield(self.snapshots.reload(), loop=self.loop)
68+
await asyncio.shield(self.snapshots.reload(), loop=self.loop)
69+
return True
6970

7071
@api_process
7172
async def info(self, request):

hassio/api/supervisor.py

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66

77
from .util import api_process, api_process_raw, api_validate
88
from ..const import (
9-
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL,
10-
HASSIO_VERSION, ATTR_ADDONS_REPOSITORIES, ATTR_REPOSITORIES,
11-
ATTR_REPOSITORY, ATTR_DESCRIPTON, ATTR_NAME, ATTR_SLUG, ATTR_INSTALLED,
12-
ATTR_DETACHED, ATTR_SOURCE, ATTR_MAINTAINER, ATTR_URL, ATTR_ARCH,
13-
ATTR_BUILD, ATTR_TIMEZONE)
9+
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL, ATTR_ARCH,
10+
HASSIO_VERSION, ATTR_ADDONS_REPOSITORIES, ATTR_LOGO, ATTR_REPOSITORY,
11+
ATTR_DESCRIPTON, ATTR_NAME, ATTR_SLUG, ATTR_INSTALLED, ATTR_TIMEZONE,
12+
ATTR_STATE, CONTENT_TYPE_BINARY)
1413
from ..tools import validate_timezone
1514

1615
_LOGGER = logging.getLogger(__name__)
@@ -41,42 +40,6 @@ def __init__(self, config, loop, supervisor, snapshots, addons,
4140
self.host_control = host_control
4241
self.websession = websession
4342

44-
def _addons_list(self, only_installed=False):
45-
"""Return a list of addons."""
46-
data = []
47-
for addon in self.addons.list_addons:
48-
if only_installed and not addon.is_installed:
49-
continue
50-
51-
data.append({
52-
ATTR_NAME: addon.name,
53-
ATTR_SLUG: addon.slug,
54-
ATTR_DESCRIPTON: addon.description,
55-
ATTR_VERSION: addon.last_version,
56-
ATTR_INSTALLED: addon.version_installed,
57-
ATTR_ARCH: addon.supported_arch,
58-
ATTR_DETACHED: addon.is_detached,
59-
ATTR_REPOSITORY: addon.repository,
60-
ATTR_BUILD: addon.need_build,
61-
ATTR_URL: addon.url,
62-
})
63-
64-
return data
65-
66-
def _repositories_list(self):
67-
"""Return a list of addons repositories."""
68-
data = []
69-
for repository in self.addons.list_repositories:
70-
data.append({
71-
ATTR_SLUG: repository.slug,
72-
ATTR_NAME: repository.name,
73-
ATTR_SOURCE: repository.source,
74-
ATTR_URL: repository.url,
75-
ATTR_MAINTAINER: repository.maintainer,
76-
})
77-
78-
return data
79-
8043
@api_process
8144
async def ping(self, request):
8245
"""Return ok for signal that the api is ready."""
@@ -85,24 +48,30 @@ async def ping(self, request):
8548
@api_process
8649
async def info(self, request):
8750
"""Return host information."""
51+
list_addons = []
52+
for addon in self.addons.list_addons:
53+
if addon.is_installed:
54+
list_addons.append({
55+
ATTR_NAME: addon.name,
56+
ATTR_SLUG: addon.slug,
57+
ATTR_DESCRIPTON: addon.description,
58+
ATTR_STATE: await addon.state(),
59+
ATTR_VERSION: addon.last_version,
60+
ATTR_INSTALLED: addon.version_installed,
61+
ATTR_REPOSITORY: addon.repository,
62+
ATTR_LOGO: addon.with_logo,
63+
})
64+
8865
return {
8966
ATTR_VERSION: HASSIO_VERSION,
9067
ATTR_LAST_VERSION: self.config.last_hassio,
9168
ATTR_BETA_CHANNEL: self.config.upstream_beta,
9269
ATTR_ARCH: self.config.arch,
9370
ATTR_TIMEZONE: self.config.timezone,
94-
ATTR_ADDONS: self._addons_list(only_installed=True),
71+
ATTR_ADDONS: list_addons,
9572
ATTR_ADDONS_REPOSITORIES: self.config.addons_repositories,
9673
}
9774

98-
@api_process
99-
async def available_addons(self, request):
100-
"""Return information for all available addons."""
101-
return {
102-
ATTR_ADDONS: self._addons_list(),
103-
ATTR_REPOSITORIES: self._repositories_list(),
104-
}
105-
10675
@api_process
10776
async def options(self, request):
10877
"""Set supervisor options."""
@@ -150,7 +119,7 @@ async def reload(self, request):
150119

151120
return True
152121

153-
@api_process_raw
122+
@api_process_raw(CONTENT_TYPE_BINARY)
154123
def logs(self, request):
155124
"""Return supervisor docker logs.
156125

0 commit comments

Comments
 (0)