diff --git a/hassio/__main__.py b/hassio/__main__.py index 4d6f0d6b9..b64bad447 100644 --- a/hassio/__main__.py +++ b/hassio/__main__.py @@ -5,7 +5,6 @@ import logging import sys import hassio.bootstrap as bootstrap -import hassio.core as core _LOGGER = logging.getLogger(__name__) @@ -34,14 +33,13 @@ if __name__ == "__main__": _LOGGER.info("Initialize Hassio setup") coresys = bootstrap.initialize_coresys(loop) - hassio = core.HassIO(coresys) bootstrap.migrate_system_env(coresys) _LOGGER.info("Setup HassIO") - loop.run_until_complete(hassio.setup()) + loop.run_until_complete(coresys.core.setup()) - loop.call_soon_threadsafe(loop.create_task, hassio.start()) + loop.call_soon_threadsafe(loop.create_task, coresys.core.start()) loop.call_soon_threadsafe(bootstrap.reg_signal, loop) try: @@ -49,7 +47,7 @@ if __name__ == "__main__": loop.run_forever() finally: _LOGGER.info("Stopping HassIO") - loop.run_until_complete(hassio.stop()) + loop.run_until_complete(coresys.core.stop()) executor.shutdown(wait=False) loop.close() diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index cfe28e6f4..4bebda235 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -376,7 +376,7 @@ class Addon(CoreSysAttributes): if self.is_installed and \ ATTR_AUDIO_OUTPUT in self._data.user[self._id]: return self._data.user[self._id][ATTR_AUDIO_OUTPUT] - return self.sys_alsa.default.output + return self.sys_host.alsa.default.output @audio_output.setter def audio_output(self, value): @@ -394,7 +394,7 @@ class Addon(CoreSysAttributes): if self.is_installed and ATTR_AUDIO_INPUT in self._data.user[self._id]: return self._data.user[self._id][ATTR_AUDIO_INPUT] - return self.sys_alsa.default.input + return self.sys_host.alsa.default.input @audio_input.setter def audio_input(self, value): @@ -537,7 +537,7 @@ class Addon(CoreSysAttributes): def write_asound(self): """Write asound config to file and return True on success.""" - asound_config = self.sys_alsa.asound( + asound_config = self.sys_host.alsa.asound( alsa_input=self.audio_input, alsa_output=self.audio_output) try: diff --git a/hassio/api/hardware.py b/hassio/api/hardware.py index 626f6d434..7830b9675 100644 --- a/hassio/api/hardware.py +++ b/hassio/api/hardware.py @@ -28,7 +28,7 @@ class APIHardware(CoreSysAttributes): """Show ALSA audio devices.""" return { ATTR_AUDIO: { - ATTR_INPUT: self.sys_alsa.input_devices, - ATTR_OUTPUT: self.sys_alsa.output_devices, + ATTR_INPUT: self.sys_host.alsa.input_devices, + ATTR_OUTPUT: self.sys_host.alsa.output_devices, } } diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index f9e0a3ac5..a2fcda3cc 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -7,6 +7,7 @@ from pathlib import Path from colorlog import ColoredFormatter +from .core import HassIO from .addons import AddonManager from .api import RestAPI from .const import SOCKET_DOCKER @@ -18,7 +19,6 @@ from .tasks import Tasks from .updater import Updater from .services import ServiceManager from .services import Discovery -from .host import AlsaAudio from .host import HostManager from .dbus import DBusManager @@ -30,9 +30,9 @@ def initialize_coresys(loop): coresys = CoreSys(loop) # Initialize core objects + coresys.core = HassIO(coresys) coresys.updater = Updater(coresys) coresys.api = RestAPI(coresys) - coresys.alsa = AlsaAudio(coresys) coresys.supervisor = Supervisor(coresys) coresys.homeassistant = HomeAssistant(coresys) coresys.addons = AddonManager(coresys) @@ -154,7 +154,12 @@ def check_environment(): # check socat exec if not shutil.which('socat'): - _LOGGER.fatal("Can0t find socat program!") + _LOGGER.fatal("Can't find socat program!") + return False + + # check socat exec + if not shutil.which('gdbus'): + _LOGGER.fatal("Can't find gdbus program!") return False return True diff --git a/hassio/const.py b/hassio/const.py index 1d318c5f4..7cdf21bb4 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -18,7 +18,6 @@ FILE_HASSIO_UPDATER = Path(HASSIO_DATA, "updater.json") FILE_HASSIO_SERVICES = Path(HASSIO_DATA, "services.json") SOCKET_DOCKER = Path("/var/run/docker.sock") -SOCKET_HC = Path("/var/run/hassio-hc.sock") DOCKER_NETWORK = 'hassio' DOCKER_NETWORK_MASK = ip_network('172.30.32.0/23') diff --git a/hassio/core.py b/hassio/core.py index ff04545f9..b537ca95e 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -23,16 +23,19 @@ class HassIO(CoreSysAttributes): if self.sys_config.timezone == 'UTC': self.sys_config.timezone = await fetch_timezone(self._websession) - # supervisor + # Load DBus + await self.sys_dbus.load() + + # Load Host + await self.sys_host.load() + + # Load Supervisor await self.sys_supervisor.load() - # hostcontrol - await self._host_control.load() - - # Load homeassistant + # Load Home Assistant await self.sys_homeassistant.load() - # Load addons + # Load Add-ons await self.sys_addons.load() # rest api views @@ -50,9 +53,6 @@ class HassIO(CoreSysAttributes): # start dns forwarding self.sys_create_task(self.sys_dns.start()) - # start addon mark as initialize - await self.sys_addons.auto_boot(STARTUP_INITIALIZE) - async def start(self): """Start HassIO orchestration.""" # on release channel, try update itself @@ -67,6 +67,9 @@ class HassIO(CoreSysAttributes): await self.sys_api.start() _LOGGER.info("Start API on %s", self.sys_docker.network.supervisor) + # start addon mark as initialize + await self.sys_addons.auto_boot(STARTUP_INITIALIZE) + try: # HomeAssistant is already running / supervisor have only reboot if self.sys_hardware.last_boot == self.sys_config.last_boot: diff --git a/hassio/coresys.py b/hassio/coresys.py index 0c0da79d1..6d16e5a81 100644 --- a/hassio/coresys.py +++ b/hassio/coresys.py @@ -32,6 +32,7 @@ class CoreSys: self._dns = DNSForward(loop=loop) # Internal objects pointers + self._core = None self._homeassistant = None self._supervisor = None self._addons = None @@ -40,9 +41,9 @@ class CoreSys: self._snapshots = None self._tasks = None self._host = None + self._dbus = None self._services = None self._discovery = None - self._alsa = None @property def arch(self): @@ -104,9 +105,16 @@ class CoreSys: return self._dns @property - def systemd(self): - """Return systemd object.""" - return self._systemd + def core(self): + """Return HassIO object.""" + return self._core + + @core.setter + def core(self, value): + """Set a HassIO object.""" + if self._core: + raise RuntimeError("HassIO already set!") + self._core = value @property def homeassistant(self): @@ -217,16 +225,16 @@ class CoreSys: self._discovery = value @property - def alsa(self): - """Return ALSA Audio object.""" - return self._alsa + def dbus(self): + """Return DBusManager object.""" + return self._dbus - @alsa.setter - def alsa(self, value): - """Set a ALSA Audio object.""" - if self._alsa: - raise RuntimeError("ALSA already set!") - self._alsa = value + @dbus.setter + def dbus(self, value): + """Set a DBusManager object.""" + if self._dbus: + raise RuntimeError("DBusManager already set!") + self._dbus = value @property def host(self): diff --git a/hassio/dbus/__init__.py b/hassio/dbus/__init__.py index a7cb5651b..4a5ae4a4e 100644 --- a/hassio/dbus/__init__.py +++ b/hassio/dbus/__init__.py @@ -7,8 +7,9 @@ from ..coresys import CoreSysAttributes class DBusManager(CoreSysAttributes): """DBus Interface handler.""" - def __init__(self): + def __init__(self, coresys): """Initialize DBus Interface.""" + self.coresys = coresys self._systemd = Systemd() @property diff --git a/hassio/dbus/hostname.py b/hassio/dbus/hostname.py new file mode 100644 index 000000000..1c25f61fa --- /dev/null +++ b/hassio/dbus/hostname.py @@ -0,0 +1 @@ +"""DBus interface for hostname.""" diff --git a/hassio/dbus/systemd.py b/hassio/dbus/systemd.py index 5089035e8..cbdb264f0 100644 --- a/hassio/dbus/systemd.py +++ b/hassio/dbus/systemd.py @@ -1,8 +1,9 @@ """Interface to Systemd over dbus.""" import logging -from ..exceptions import HassioInternalError -from ..utils.gdbus import DBus, DBusError +from .utils import dbus_connected +from ..exceptions import DBusError +from ..utils.gdbus import DBus _LOGGER = logging.getLogger(__name__) @@ -29,18 +30,18 @@ class Systemd: except DBusError: _LOGGER.warning("Can't connect to systemd") - async def reboot(self): - """Reboot host computer.""" - try: - await self.dbus.Manager.Reboot() - except DBusError: - _LOGGER.error("Can't reboot host") - raise HassioInternalError() from None + @dbus_connected + def reboot(self): + """Reboot host computer. - async def shutdown(self): - """Shutdown host computer.""" - try: - await self.dbus.Manager.PowerOff() - except DBusError: - _LOGGER.error("Can't PowerOff host") - raise HassioInternalError() from None + Return a coroutine. + """ + return self.dbus.Manager.Reboot() + + @dbus_connected + def shutdown(self): + """Shutdown host computer. + + Return a coroutine. + """ + return self.dbus.Manager.PowerOff() diff --git a/hassio/dbus/utils.py b/hassio/dbus/utils.py new file mode 100644 index 000000000..7c05bb174 --- /dev/null +++ b/hassio/dbus/utils.py @@ -0,0 +1,14 @@ +"""Utils for dbus.""" + +from ..exceptions import HassioNotSupportedError + + +def dbus_connected(method): + """Wrapper for check if dbus is connected.""" + def wrap_dbus(self, *args, **kwargs): + """Check if dbus is connected before call a method.""" + if self.dbus is None: + raise HassioNotSupportedError(f"{self!s} not connected to dbus!") + return self.method(*args, **kwargs) + + return wrap_dbus diff --git a/hassio/host/__init__.py b/hassio/host/__init__.py index 1080ac6eb..ef8d997e1 100644 --- a/hassio/host/__init__.py +++ b/hassio/host/__init__.py @@ -1,6 +1,7 @@ """Host function like audio/dbus/systemd.""" -from .alsa import AlsaAudio # noqa +from .alsa import AlsaAudio +from .power import PowerControl from ..const import FEATURES_REBOOT, FEATURES_SHUTDOWN from ..coresys import CoreSysAttributes @@ -11,13 +12,25 @@ class HostManager(CoreSysAttributes): def __init__(self, coresys): """Initialize Host manager.""" self.coresys = coresys + self._alsa = AlsaAudio(coresys) + self._power = PowerControl(coresys) + + @property + def alsa(self): + """Return host ALSA handler.""" + return self._alsa + + @property + def power(self): + """Return host power handler.""" + return self._power @property def supperted_features(self): """Return a list of supported host features.""" features = [] - if self.sys_systemd.is_connected: + if self.sys_dbus.systemd.is_connected: features.extend([ FEATURES_REBOOT, FEATURES_SHUTDOWN, @@ -27,4 +40,4 @@ class HostManager(CoreSysAttributes): async def load(self): """Load host functions.""" - await self.sys_systemd.connect() + pass diff --git a/hassio/host/power.py b/hassio/host/power.py new file mode 100644 index 000000000..880d7430a --- /dev/null +++ b/hassio/host/power.py @@ -0,0 +1,33 @@ +"""Power control for host.""" + +from ..coresys import CoreSysAttributes +from ..exceptions import HassioNotSupportedError + + +class PowerControl(CoreSysAttributes): + """Handle host power controls.""" + + def __init__(self, coresys): + """Initialize host power handling.""" + self.coresys = coresys + + def _check_systemd(self): + """Check if systemd is connect or raise error.""" + if not self.sys_dbus.systemd.is_connected: + raise HassioNotSupportedError("No systemd connections") + + async def reboot(self): + """Reboot host system.""" + self._check_systemd() + try: + await self.sys_core.shutdown() + finally: + await self.sys_dbus.systemd.reboot() + + async def shutdown(self): + """Shutdown host system.""" + self._check_systemd() + try: + await self.sys_core.shutdown() + finally: + await self.sys_dbus.systemd.shutdown()