Skip to content

Commit

Permalink
Merge pull request #23 from kvj/ubus_service
Browse files Browse the repository at this point in the history
Ubus service
  • Loading branch information
kvj authored Jan 22, 2024
2 parents 13e9ba7 + b656fcf commit a59a144
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 18 deletions.
45 changes: 34 additions & 11 deletions custom_components/openwrt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations
from .constants import DOMAIN, PLATFORMS

from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, SupportsResponse
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers import service
from homeassistant.helpers.update_coordinator import (
Expand Down Expand Up @@ -53,15 +53,23 @@ async def async_reboot(call):

async def async_exec(call):
parts = call.data["command"].split(" ")
for entry_id in await service.async_extract_config_entry_ids(hass, call):
device = hass.data[DOMAIN]["devices"][entry_id]
if device.is_api_supported("file"):
await device.do_file_exec(
parts[0],
parts[1:],
call.data.get("environment", {}),
call.data.get("extra", {})
)
ids = await service.async_extract_config_entry_ids(hass, call)
response = {}
for entry_id in ids:
if coordinator := hass.data[DOMAIN]["devices"].get(entry_id):
if coordinator.is_api_supported("file"):
args = parts[1:]
if "arguments" in call.data:
args = call.data["arguments"].strip().split("\n")
response[entry_id] = await coordinator.do_file_exec(
parts[0],
args,
call.data.get("environment", {}),
call.data.get("extra", {})
)
if len(ids) == 1:
return response.get(list(ids)[0])
return response

async def async_init(call):
parts = call.data["name"].split(" ")
Expand All @@ -73,9 +81,24 @@ async def async_init(call):
call.data.get("action", {})
)

async def async_ubus(call):
response = {}
ids = await service.async_extract_config_entry_ids(hass, call)
for entry_id in ids:
if coordinator := hass.data[DOMAIN]["devices"].get(entry_id):
response[entry_id] = await coordinator.do_ubus_call(
call.data.get("subsystem"),
call.data.get("method"),
call.data.get("parameters", {}),
)
if len(ids) == 1:
return response.get(list(ids)[0])
return response

hass.services.async_register(DOMAIN, "reboot", async_reboot)
hass.services.async_register(DOMAIN, "exec", async_exec)
hass.services.async_register(DOMAIN, "exec", async_exec, supports_response=SupportsResponse.OPTIONAL)
hass.services.async_register(DOMAIN, "init", async_init)
hass.services.async_register(DOMAIN, "ubus", async_ubus, supports_response=SupportsResponse.ONLY)

return True

Expand Down
3 changes: 3 additions & 0 deletions custom_components/openwrt/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from homeassistant import config_entries
import homeassistant.helpers.config_validation as cv
from .constants import DOMAIN
from .coordinator import new_ubus_client

import logging
import voluptuous as vol
Expand Down Expand Up @@ -36,5 +37,7 @@ async def async_step_user(self, user_input):
_LOGGER.debug(f"Input: {user_input}")
await self.async_set_unique_id(user_input["address"])
self._abort_if_unique_id_configured()
ubus = new_ubus_client(self.hass, user_input)
await ubus.api_list() # Check connection
title = "%s - %s" % (user_input["id"], user_input["address"])
return self.async_create_entry(title=title, data=user_input)
31 changes: 26 additions & 5 deletions custom_components/openwrt/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
DataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.util.json import json_loads

from .ubus import Ubus
from .constants import DOMAIN
Expand Down Expand Up @@ -180,6 +181,23 @@ async def do_file_exec(self, command: str, params, env: dict, extra: dict):
**extra,
},
)
def process_output(data: str):
try:
json = json_loads(data)
if type(json) is list or type(json) is dict:
return json
except:
pass
return data.strip().split("\n")
return {
"code": result.get("code", 1),
"stdout": process_output(result.get("stdout", "")),
"stderr": process_output(result.get("stderr", "")),
}

async def do_ubus_call(self, subsystem: str, method: str, params: dict):
_LOGGER.debug(f"do_ubus_call(): {subsystem} / {method}: {params}")
return await self._ubus.api_call(subsystem, method, params)

async def do_rc_init(self, name: str, action: str):
_LOGGER.debug(
Expand Down Expand Up @@ -262,7 +280,7 @@ async def update_wan_info(self):
return result

async def load_ubus(self):
return await self._ubus.api_call("*", None, None, "list")
return await self._ubus.api_list()

def is_api_supported(self, name: str) -> bool:
if self._apis and name in self._apis:
Expand Down Expand Up @@ -291,18 +309,21 @@ async def async_update_data():
raise UpdateFailed(f"OpenWrt communication error: {err}")
return async_update_data


def new_coordinator(hass, config: dict, all_devices: dict) -> DeviceCoordinator:
_LOGGER.debug(f"new_coordinator: {config}")
def new_ubus_client(hass, config: dict) -> Ubus:
_LOGGER.debug(f"new_ubus_client(): {config}")
schema = "https" if config["https"] else "http"
port = ":%d" % (config["port"]) if config["port"] > 0 else ''
url = "%s://%s%s%s" % (schema, config["address"], port, config["path"])
connection = Ubus(
return Ubus(
hass.async_add_executor_job,
url,
config["username"],
config.get("password", ""),
verify=config.get("verify_cert", True)
)

def new_coordinator(hass, config: dict, all_devices: dict) -> DeviceCoordinator:
_LOGGER.debug(f"new_coordinator: {config}, {all_devices}")
connection = new_ubus_client(hass, config)
device = DeviceCoordinator(hass, config, connection, all_devices)
return device
2 changes: 1 addition & 1 deletion custom_components/openwrt/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"requirements": [],
"iot_class": "local_polling",
"config_flow": true,
"version": "0.0.2"
"version": "0.1.0"
}

35 changes: 34 additions & 1 deletion custom_components/openwrt/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ exec:
example: "wifi reload"
selector:
text: {}
arguments:
name: Command arguments
description: Arguments to append to the command (one per line)
required: false
selector:
text:
multiline: true
environment:
name: Environment variables
description: Map of Environment variables names with values
Expand Down Expand Up @@ -55,4 +62,30 @@ init:
- "restart"
- "reload"
- "enable"
- "disable"
- "disable"
ubus:
name: Make arbitrary Ubus call
target:
device:
integration: openwrt
fields:
subsystem:
name: Ubus sub-system
description: Top-level Ubus sub-system
required: true
example: "system"
selector:
text: {}
method:
name: Ubus method
description: Ubus method to call
required: true
example: "board"
selector:
text: {}
parameters:
name: Call parameters
description: Ubus call paramteres
required: false
selector:
object: {}
3 changes: 3 additions & 0 deletions custom_components/openwrt/ubus.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,6 @@ def post():
if result_code == 0:
return json_response['result'][1] if len(result) > 1 else {}
raise ConnectionError(f"rpc error: {result[0]}")

async def api_list(self):
return await self.api_call("*", None, None, "list")

0 comments on commit a59a144

Please sign in to comment.