Skip to content

Commit a59a144

Browse files
authored
Merge pull request #23 from kvj/ubus_service
Ubus service
2 parents 13e9ba7 + b656fcf commit a59a144

File tree

6 files changed

+101
-18
lines changed

6 files changed

+101
-18
lines changed

custom_components/openwrt/__init__.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22
from .constants import DOMAIN, PLATFORMS
33

4-
from homeassistant.core import HomeAssistant
4+
from homeassistant.core import HomeAssistant, SupportsResponse
55
from homeassistant.helpers.typing import ConfigType
66
from homeassistant.helpers import service
77
from homeassistant.helpers.update_coordinator import (
@@ -53,15 +53,23 @@ async def async_reboot(call):
5353

5454
async def async_exec(call):
5555
parts = call.data["command"].split(" ")
56-
for entry_id in await service.async_extract_config_entry_ids(hass, call):
57-
device = hass.data[DOMAIN]["devices"][entry_id]
58-
if device.is_api_supported("file"):
59-
await device.do_file_exec(
60-
parts[0],
61-
parts[1:],
62-
call.data.get("environment", {}),
63-
call.data.get("extra", {})
64-
)
56+
ids = await service.async_extract_config_entry_ids(hass, call)
57+
response = {}
58+
for entry_id in ids:
59+
if coordinator := hass.data[DOMAIN]["devices"].get(entry_id):
60+
if coordinator.is_api_supported("file"):
61+
args = parts[1:]
62+
if "arguments" in call.data:
63+
args = call.data["arguments"].strip().split("\n")
64+
response[entry_id] = await coordinator.do_file_exec(
65+
parts[0],
66+
args,
67+
call.data.get("environment", {}),
68+
call.data.get("extra", {})
69+
)
70+
if len(ids) == 1:
71+
return response.get(list(ids)[0])
72+
return response
6573

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

84+
async def async_ubus(call):
85+
response = {}
86+
ids = await service.async_extract_config_entry_ids(hass, call)
87+
for entry_id in ids:
88+
if coordinator := hass.data[DOMAIN]["devices"].get(entry_id):
89+
response[entry_id] = await coordinator.do_ubus_call(
90+
call.data.get("subsystem"),
91+
call.data.get("method"),
92+
call.data.get("parameters", {}),
93+
)
94+
if len(ids) == 1:
95+
return response.get(list(ids)[0])
96+
return response
97+
7698
hass.services.async_register(DOMAIN, "reboot", async_reboot)
77-
hass.services.async_register(DOMAIN, "exec", async_exec)
99+
hass.services.async_register(DOMAIN, "exec", async_exec, supports_response=SupportsResponse.OPTIONAL)
78100
hass.services.async_register(DOMAIN, "init", async_init)
101+
hass.services.async_register(DOMAIN, "ubus", async_ubus, supports_response=SupportsResponse.ONLY)
79102

80103
return True
81104

custom_components/openwrt/config_flow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from homeassistant import config_entries
22
import homeassistant.helpers.config_validation as cv
33
from .constants import DOMAIN
4+
from .coordinator import new_ubus_client
45

56
import logging
67
import voluptuous as vol
@@ -36,5 +37,7 @@ async def async_step_user(self, user_input):
3637
_LOGGER.debug(f"Input: {user_input}")
3738
await self.async_set_unique_id(user_input["address"])
3839
self._abort_if_unique_id_configured()
40+
ubus = new_ubus_client(self.hass, user_input)
41+
await ubus.api_list() # Check connection
3942
title = "%s - %s" % (user_input["id"], user_input["address"])
4043
return self.async_create_entry(title=title, data=user_input)

custom_components/openwrt/coordinator.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
DataUpdateCoordinator,
55
UpdateFailed,
66
)
7+
from homeassistant.util.json import json_loads
78

89
from .ubus import Ubus
910
from .constants import DOMAIN
@@ -180,6 +181,23 @@ async def do_file_exec(self, command: str, params, env: dict, extra: dict):
180181
**extra,
181182
},
182183
)
184+
def process_output(data: str):
185+
try:
186+
json = json_loads(data)
187+
if type(json) is list or type(json) is dict:
188+
return json
189+
except:
190+
pass
191+
return data.strip().split("\n")
192+
return {
193+
"code": result.get("code", 1),
194+
"stdout": process_output(result.get("stdout", "")),
195+
"stderr": process_output(result.get("stderr", "")),
196+
}
197+
198+
async def do_ubus_call(self, subsystem: str, method: str, params: dict):
199+
_LOGGER.debug(f"do_ubus_call(): {subsystem} / {method}: {params}")
200+
return await self._ubus.api_call(subsystem, method, params)
183201

184202
async def do_rc_init(self, name: str, action: str):
185203
_LOGGER.debug(
@@ -262,7 +280,7 @@ async def update_wan_info(self):
262280
return result
263281

264282
async def load_ubus(self):
265-
return await self._ubus.api_call("*", None, None, "list")
283+
return await self._ubus.api_list()
266284

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

294-
295-
def new_coordinator(hass, config: dict, all_devices: dict) -> DeviceCoordinator:
296-
_LOGGER.debug(f"new_coordinator: {config}")
312+
def new_ubus_client(hass, config: dict) -> Ubus:
313+
_LOGGER.debug(f"new_ubus_client(): {config}")
297314
schema = "https" if config["https"] else "http"
298315
port = ":%d" % (config["port"]) if config["port"] > 0 else ''
299316
url = "%s://%s%s%s" % (schema, config["address"], port, config["path"])
300-
connection = Ubus(
317+
return Ubus(
301318
hass.async_add_executor_job,
302319
url,
303320
config["username"],
304321
config.get("password", ""),
305322
verify=config.get("verify_cert", True)
306323
)
324+
325+
def new_coordinator(hass, config: dict, all_devices: dict) -> DeviceCoordinator:
326+
_LOGGER.debug(f"new_coordinator: {config}, {all_devices}")
327+
connection = new_ubus_client(hass, config)
307328
device = DeviceCoordinator(hass, config, connection, all_devices)
308329
return device

custom_components/openwrt/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
"requirements": [],
99
"iot_class": "local_polling",
1010
"config_flow": true,
11-
"version": "0.0.2"
11+
"version": "0.1.0"
1212
}
1313

custom_components/openwrt/services.yaml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ exec:
1616
example: "wifi reload"
1717
selector:
1818
text: {}
19+
arguments:
20+
name: Command arguments
21+
description: Arguments to append to the command (one per line)
22+
required: false
23+
selector:
24+
text:
25+
multiline: true
1926
environment:
2027
name: Environment variables
2128
description: Map of Environment variables names with values
@@ -55,4 +62,30 @@ init:
5562
- "restart"
5663
- "reload"
5764
- "enable"
58-
- "disable"
65+
- "disable"
66+
ubus:
67+
name: Make arbitrary Ubus call
68+
target:
69+
device:
70+
integration: openwrt
71+
fields:
72+
subsystem:
73+
name: Ubus sub-system
74+
description: Top-level Ubus sub-system
75+
required: true
76+
example: "system"
77+
selector:
78+
text: {}
79+
method:
80+
name: Ubus method
81+
description: Ubus method to call
82+
required: true
83+
example: "board"
84+
selector:
85+
text: {}
86+
parameters:
87+
name: Call parameters
88+
description: Ubus call paramteres
89+
required: false
90+
selector:
91+
object: {}

custom_components/openwrt/ubus.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,6 @@ def post():
119119
if result_code == 0:
120120
return json_response['result'][1] if len(result) > 1 else {}
121121
raise ConnectionError(f"rpc error: {result[0]}")
122+
123+
async def api_list(self):
124+
return await self.api_call("*", None, None, "list")

0 commit comments

Comments
 (0)