async def attr_(obj, bus, group, vars_, eval_, path_): """Set/get/delete an attribute on a given KNX element. `--eval` without a value deletes the attribute. """ group = (int(x) for x in group.split("/")) if group else () path = Path(bus, *group) if len(path) != 4: raise click.UsageError("Group address must be 3 /-separated elements.") res = await obj.client.get(obj.cfg.knx.prefix + path, nchain=obj.meta or 1) if vars_ or eval_ or path_: res = await node_attr(obj, obj.cfg.knx.prefix + path, vars_, eval_, path_, res=res) if obj.meta: yprint(res, stream=obj.stdout) else: if not obj.meta: res = res.value yprint(res, stream=obj.stdout)
async def addr(obj, bus, group, typ, mode, attr): """Set/get/delete device settings. This is a shortcut for the "attr" command.""" group = (int(x) for x in group.split("/")) path = Path(bus, *group) if len(path) != 4: raise click.UsageError("Group address must be 3 /-separated elements.") res = await obj.client.get(obj.cfg.knx.prefix + path, nchain=obj.meta or 1) val = res.get("value", attrdict()) if typ == "-": res = await obj.client.delete(obj.cfg.knx.prefix + path, nchain=obj.meta) if obj.meta: yprint(res, stream=obj.stdout) return if typ: attr = (("type", typ), ) + attr if mode: attr = (("mode", mode), ) + attr for k, v in attr: if k in {"src", "dest"}: v = P(v) else: try: v = int(v) except ValueError: try: v = float(v) except ValueError: pass val[k] = v await _attr(obj, (), val, path, False, res)
async def server_(obj, name, host, port, delete): """ Configure a server. No arguments: list them. """ if not name: if host or port or delete: raise click.UsageError("Use a server name to set parameters") async for r in obj.client.get_tree(obj.cfg.akumuli.prefix, min_depth=1, max_depth=1): print(r.path[-1], file=obj.stdout) return elif len(name) > 1: raise click.UsageError("Only one server allowed") name = name[0] if host or port: if delete: raise click.UsageError( "You can't delete and set at the same time.") value = attrdict() if host: value.host = host if port: if port == "-": value.port = NotGiven else: value.port = int(port) elif delete: res = await obj.client.delete_tree(obj.cfg.akumuli.prefix + name, nchain=obj.meta) if obj.meta: yprint(res, stream=obj.stdout) return else: value = None res = await node_attr(obj, obj.cfg.akumuli.prefix | name, ("server", ), value) if obj.meta: yprint(res, stream=obj.stdout)
async def attr_(obj, attr, value, path, eval_, path_): """Set/get/delete an attribute on a given akumuli element. `--eval` without a value deletes the attribute. """ path = P(path) if path_ and eval_: raise click.UsageError("split and eval don't work together.") if value and not attr: raise click.UsageError("Values must have locations ('-a ATTR').") if path_: value = P(value) res = await node_attr(obj, obj.cfg.akumuli.prefix + path, P(attr), value, eval_=eval_) if obj.meta: yprint(res, stream=obj.stdout)
async def list_(obj, path): """List the next stage.""" path = P(path) if len(path) > 4: raise click.UsageError("Only up to four path elements allowed") async for r in obj.client.get_tree(obj.cfg.knx.prefix + path, nchain=obj.meta, min_depth=1, max_depth=1, empty=True): if len(path) and not isinstance(r.path[-1], int): continue print(r.path[-1], file=obj.stdout)
async def set_(obj, path, source, mode, attr, series, tags): """Set/delete part of a series. \b path: the name of this copy command. Unique path, non-empty. source: the element with the data. unique path, non-empty. series: the Akumuli series to write to. tags: any number of "name=value" Akumuli tags to use for the series. A series of '-' deletes. """ path = P(path) attr = P(attr) source = P(source) mode = getattr(DS, mode) tagged = {} if series == "-": if tags: raise click.UsageError("You can't add tags when deleting") series = None await obj.client.delete(obj.cfg.akumuli.prefix + path) return if not tags: raise click.UsageError("You can't write to a series without tags") for x in tags: try: k, v = x.split("=", 2) except ValueError: raise click.UsageError("Tags must be key=value") from None tagged[k] = v val = dict(source=source, series=series, tags=tagged, mode=mode.name) if attr: val["attr"] = attr res = await obj.client.set(obj.cfg.akumuli.prefix + path, val) if obj.meta: yprint(res, stream=obj.stdout)
async def dump(obj, path): """Emit the current state as a YAML file.""" res = {} path = P(path) if len(path) > 4: raise click.UsageError("Only up to four path elements allowed") async for r in obj.client.get_tree(obj.cfg.knx.prefix + path, nchain=obj.meta, max_depth=4 - len(path)): # pl = len(path) + len(r.path) rr = res if r.path: for rp in r.path: rr = rr.setdefault(rp, {}) rr["_"] = r if obj.meta else r.value yprint(res, stream=obj.stdout)
async def monitor(obj, bus, server, local_ip, initial): """Stand-alone task to talk to a single server.""" from distkv_ext.knx.task import task from distkv_ext.knx.model import KNXroot knx = await KNXroot.as_handler(obj.client) await knx.wait_loaded() if not server and " " in bus: bus, server = bus.split(" ") elif len(server) != 1: raise click.UsageError("Use a single server name") else: server = server[0] async with as_service(obj) as srv: await task(obj.client, obj.cfg.knx, knx[bus][server], srv, local_ip=local_ip, initial=initial)
async def run(obj, checks, as_list): """ Run a one-shot call. """ if not checks: checks = list(k for k,v in obj.calls.items() if as_list or not v.test.skip) if not checks: raise click.UsageError("No tests known. Missing config file?") if as_list: for c in checks: c = obj.calls[c] print(c.name, "m" if c.test.skip else "-", c.info, sep="\t") return ast = obj.cfg.asterisk url = "http://%s:%d/" % (ast.host,ast.port) async with asyncari.connect(url, ast.app, username=ast.username, password=ast.password) as client: client._calltest_config = obj.cfg async with anyio.create_task_group() as tg: for c in checks: await tg.spawn(obj.calls[c], client)