mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-11-10 11:29:51 +00:00
Add systemd dbus
This commit is contained in:
@@ -11,6 +11,7 @@ from voluptuous.humanize import humanize_error
|
|||||||
from ..const import (
|
from ..const import (
|
||||||
JSON_RESULT, JSON_DATA, JSON_MESSAGE, RESULT_OK, RESULT_ERROR,
|
JSON_RESULT, JSON_DATA, JSON_MESSAGE, RESULT_OK, RESULT_ERROR,
|
||||||
CONTENT_TYPE_BINARY)
|
CONTENT_TYPE_BINARY)
|
||||||
|
from ..exceptions import HassioError
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -33,6 +34,8 @@ def api_process(method):
|
|||||||
answer = await method(api, *args, **kwargs)
|
answer = await method(api, *args, **kwargs)
|
||||||
except RuntimeError as err:
|
except RuntimeError as err:
|
||||||
return api_return_error(message=str(err))
|
return api_return_error(message=str(err))
|
||||||
|
except HassioError:
|
||||||
|
return api_return_error()
|
||||||
|
|
||||||
if isinstance(answer, dict):
|
if isinstance(answer, dict):
|
||||||
return api_return_ok(data=answer)
|
return api_return_ok(data=answer)
|
||||||
@@ -45,30 +48,6 @@ def api_process(method):
|
|||||||
return wrap_api
|
return wrap_api
|
||||||
|
|
||||||
|
|
||||||
def api_process_hostcontrol(method):
|
|
||||||
"""Wrap HostControl calls to rest api."""
|
|
||||||
async def wrap_hostcontrol(api, *args, **kwargs):
|
|
||||||
"""Return host information."""
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
if not api._host_control.active:
|
|
||||||
raise HTTPServiceUnavailable()
|
|
||||||
|
|
||||||
try:
|
|
||||||
answer = await method(api, *args, **kwargs)
|
|
||||||
except RuntimeError as err:
|
|
||||||
return api_return_error(message=str(err))
|
|
||||||
|
|
||||||
if isinstance(answer, dict):
|
|
||||||
return api_return_ok(data=answer)
|
|
||||||
elif answer is None:
|
|
||||||
return api_return_error("Function is not supported")
|
|
||||||
elif answer:
|
|
||||||
return api_return_ok()
|
|
||||||
return api_return_error()
|
|
||||||
|
|
||||||
return wrap_hostcontrol
|
|
||||||
|
|
||||||
|
|
||||||
def api_process_raw(content):
|
def api_process_raw(content):
|
||||||
"""Wrap content_type into function."""
|
"""Wrap content_type into function."""
|
||||||
def wrap_method(method):
|
def wrap_method(method):
|
||||||
@@ -81,6 +60,9 @@ def api_process_raw(content):
|
|||||||
except RuntimeError as err:
|
except RuntimeError as err:
|
||||||
msg_data = str(err).encode()
|
msg_data = str(err).encode()
|
||||||
msg_type = CONTENT_TYPE_BINARY
|
msg_type = CONTENT_TYPE_BINARY
|
||||||
|
except HassioError:
|
||||||
|
msg_data = b''
|
||||||
|
msg_type = CONTENT_TYPE_BINARY
|
||||||
|
|
||||||
return web.Response(body=msg_data, content_type=msg_type)
|
return web.Response(body=msg_data, content_type=msg_type)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Interface to Systemd over dbus."""
|
"""Interface to Systemd over dbus."""
|
||||||
from logging
|
import logging
|
||||||
|
|
||||||
from ..exceptions import HassioInternalError
|
from ..exceptions import HassioInternalError
|
||||||
from ..utils.gdbus import DBus, DBusError
|
from ..utils.gdbus import DBus, DBusError
|
||||||
@@ -7,7 +7,7 @@ from ..utils.gdbus import DBus, DBusError
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DBUS_NAME = 'org.freedesktop.systemd1'
|
DBUS_NAME = 'org.freedesktop.systemd1'
|
||||||
DBUS_OBJECT = '/org/freedesktop/systemd1/Manager'
|
DBUS_OBJECT = '/org/freedesktop/systemd1'
|
||||||
|
|
||||||
|
|
||||||
class Systemd(object):
|
class Systemd(object):
|
||||||
@@ -17,18 +17,22 @@ class Systemd(object):
|
|||||||
"""Initialize systemd."""
|
"""Initialize systemd."""
|
||||||
self.dbus = None
|
self.dbus = None
|
||||||
|
|
||||||
async def load(self):
|
@property
|
||||||
|
def is_connected(self):
|
||||||
|
"""Return True, if they is connected to dbus."""
|
||||||
|
return self.dbus is not None
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
"""Connect do bus."""
|
"""Connect do bus."""
|
||||||
try:
|
try:
|
||||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||||
except DBusError:
|
except DBusError:
|
||||||
_LOGGER.warning("Can't connect to systemd")
|
_LOGGER.warning("Can't connect to systemd")
|
||||||
return
|
|
||||||
|
|
||||||
async def reboot(self):
|
async def reboot(self):
|
||||||
"""Reboot host computer."""
|
"""Reboot host computer."""
|
||||||
try:
|
try:
|
||||||
await self.dbus.Reboot()
|
await self.dbus.Manager.Reboot()
|
||||||
except DBusError:
|
except DBusError:
|
||||||
_LOGGER.error("Can't reboot host")
|
_LOGGER.error("Can't reboot host")
|
||||||
raise HassioInternalError() from None
|
raise HassioInternalError() from None
|
||||||
@@ -36,7 +40,7 @@ class Systemd(object):
|
|||||||
async def shutdown(self):
|
async def shutdown(self):
|
||||||
"""Shutdown host computer."""
|
"""Shutdown host computer."""
|
||||||
try:
|
try:
|
||||||
await self.dbus.PowerOff()
|
await self.dbus.Manager.PowerOff()
|
||||||
except DBusError:
|
except DBusError:
|
||||||
_LOGGER.error("Can't PowerOff host")
|
_LOGGER.error("Can't PowerOff host")
|
||||||
raise HassioInternalError() from None
|
raise HassioInternalError() from None
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
INTROSPECT = ("gdbus introspect --system --dest {bus} "
|
INTROSPECT = ("gdbus introspect --system --dest {bus} "
|
||||||
"--object-path {obj} --xml")
|
"--object-path {obj} --xml")
|
||||||
CALL = ("gdbus call --system --dest {bus} --object-path {obj} "
|
CALL = ("gdbus call --system --dest {bus} --object-path {inf} "
|
||||||
"--method {obj}.{method} {args}")
|
"--method {inf}.{method} {args}")
|
||||||
|
|
||||||
|
|
||||||
class DBusError(Exception):
|
class DBusError(Exception):
|
||||||
@@ -39,7 +39,7 @@ class DBus(object):
|
|||||||
"""Initialize dbus object."""
|
"""Initialize dbus object."""
|
||||||
self.bus_name = bus_name
|
self.bus_name = bus_name
|
||||||
self.object_path = object_path
|
self.object_path = object_path
|
||||||
self.methods = []
|
self.data = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def connect(bus_name, object_path):
|
async def connect(bus_name, object_path):
|
||||||
@@ -73,19 +73,22 @@ class DBus(object):
|
|||||||
raise DBusParseError() from None
|
raise DBusParseError() from None
|
||||||
|
|
||||||
# Read available methods
|
# Read available methods
|
||||||
for method in xml.findall(".//method"):
|
for interface in xml.findall("/node/interface"):
|
||||||
self.methods.append(method.get('name'))
|
methods = []
|
||||||
|
for method in interface.findall("/method"):
|
||||||
|
methods.append(method.get('name'))
|
||||||
|
self.data[interface.get('name')] = methods
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _gvariant(raw):
|
def _gvariant(raw):
|
||||||
"""Parse GVariant input to python."""
|
"""Parse GVariant input to python."""
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
async def _call_dbus(self, method, *args):
|
async def call_dbus(self, interface, method, *args):
|
||||||
"""Call a dbus method."""
|
"""Call a dbus method."""
|
||||||
command = shlex.split(CALL.format(
|
command = shlex.split(CALL.format(
|
||||||
bus=self.bus_name,
|
bus=self.bus_name,
|
||||||
obj=self.object_path,
|
inf=interface,
|
||||||
method=method,
|
method=method,
|
||||||
args=" ".join(map(str, args))
|
args=" ".join(map(str, args))
|
||||||
))
|
))
|
||||||
@@ -124,9 +127,26 @@ class DBus(object):
|
|||||||
# End
|
# End
|
||||||
return data.decode()
|
return data.decode()
|
||||||
|
|
||||||
|
def __getattr__(self, interface):
|
||||||
|
"""Mapping to dbus method."""
|
||||||
|
interface = f"{self.object_path}.{interface}"
|
||||||
|
if interface not in self.data:
|
||||||
|
raise AttributeError()
|
||||||
|
|
||||||
|
return DBusCallWrapper(self, interface)
|
||||||
|
|
||||||
|
|
||||||
|
class DBusCallWrapper(object):
|
||||||
|
"""Wrapper a DBus interface for a call."""
|
||||||
|
|
||||||
|
def __init__(self, dbus, interface):
|
||||||
|
"""Initialize wrapper."""
|
||||||
|
self.dbus = dbus
|
||||||
|
self.interface = interface
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
"""Mapping to dbus method."""
|
"""Mapping to dbus method."""
|
||||||
if name not in self.methods:
|
if name not in self.dbus.data[self.interface]:
|
||||||
raise AttributeError()
|
raise AttributeError()
|
||||||
|
|
||||||
def _method_wrapper(*args):
|
def _method_wrapper(*args):
|
||||||
@@ -134,6 +154,6 @@ class DBus(object):
|
|||||||
|
|
||||||
Return a coroutine
|
Return a coroutine
|
||||||
"""
|
"""
|
||||||
return self._call_dbus(name, *args)
|
return self.dbus.call_dbus(self.interface, self.name, *args)
|
||||||
|
|
||||||
return _method_wrapper
|
return _method_wrapper
|
||||||
|
|||||||
Reference in New Issue
Block a user