/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2023 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./getnodes.h" #include "../utils.h" #include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/ipc/nodes/msg_ipc_getdevices_req.h" #include "aqhome/ipc/nodes/msg_ipc_getdevices_rsp.h" #include "aqhome/ipc/msg_ipc_result.h" #include "aqhome/msg/msg_node.h" #include #include #include #include #include #include #include #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) static int _doGetNodes(GWEN_DB_NODE *dbArgs); static int _sendGetDevices(GWEN_MSG_ENDPOINT *epTcp); static void _printNodeFromMsg(const GWEN_MSG *msg); static void _printUintAsTextOrHex(uint32_t u, int bits); int AQH_Tool_GetNodes(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { GWEN_DB_NODE *dbLocalArgs; int rv; const GWEN_ARGS args[]= { { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ "tcpAddress", /* name */ 0, /* minnum */ 1, /* maxnum */ "t", /* short option */ "tcpaddress", /* long option */ I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "tcpPort", /* name */ 0, /* minnum */ 1, /* maxnum */ "P", /* short option */ "tcpport", /* long option */ I18S("Specify the TCP port to listen on"), I18S("Specify the TCP port to listen on") }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Int, /* type */ "timeout", /* name */ 0, /* minnum */ 1, /* maxnum */ "T", /* short option */ NULL, /* long option */ I18S("Specify timeout in seconds for PONG response"), I18S("Specify timeout in seconds for PONG response") }, { GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ GWEN_ArgsType_Int, /* type */ "help", /* name */ 0, /* minnum */ 0, /* maxnum */ "h", /* short option */ "help", /* long option */ "Show this help screen", /* short description */ "Show this help screen" /* long description */ } }; dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); rv=GWEN_Args_Check(argc, argv, 1, GWEN_ARGS_MODE_ALLOW_FREEPARAM, args, dbLocalArgs); if (rv==GWEN_ARGS_RESULT_ERROR) { fprintf(stderr, "ERROR: Could not parse arguments\n"); return 1; } else if (rv==GWEN_ARGS_RESULT_HELP) { GWEN_BUFFER *ubuf; ubuf=GWEN_Buffer_new(0, 1024, 0, 1); if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { fprintf(stderr, "ERROR: Could not create help string\n"); return 1; } fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); GWEN_Buffer_free(ubuf); return 0; } return _doGetNodes(dbLocalArgs); } int _doGetNodes(GWEN_DB_NODE *dbArgs) { GWEN_MSG_ENDPOINT *epTcp; int rv; int timeoutInSeconds; epTcp=Utils_SetupNodesClientEndpoint(dbArgs); if (epTcp==NULL) { DBG_ERROR(NULL, "ERROR creating TCP connection"); return 2; } timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); rv=Utils_SendAcceptedMsgGroups(epTcp, 0); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return 2; } rv=_sendGetDevices(epTcp); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return 2; } for (;;) { GWEN_MSG *msg; uint16_t code; msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP, timeoutInSeconds); if (msg==NULL) { DBG_INFO(NULL, "No GET_DEVICE response received."); GWEN_MsgEndpoint_free(epTcp); return 2; } code=GWEN_IpcMsg_GetCode(msg); if (code==AQH_MSGTYPE_IPC_NODES_RESULT) { fprintf(stdout, "No device list (%d)\n", AQH_ResultIpcMsg_GetResultCode(msg)); GWEN_Msg_free(msg); break; } else if (code==AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP) { uint8_t flags; flags=AQH_GetDevicesResponseIpcMsg_GetFlags(msg); _printNodeFromMsg(msg); GWEN_Msg_free(msg); if (flags & AQH_MSGIPC_GETDEVICES_RSP_FLAGS_LAST) { DBG_INFO(NULL, "Last"); break; } } else { GWEN_Msg_free(msg); return GWEN_ERROR_GENERIC; } } GWEN_MsgEndpoint_free(epTcp); return 0; } void _printNodeFromMsg(const GWEN_MSG *msg) { uint32_t u; int64_t ts64; GWEN_TIMESTAMP *ts; ts64=AQH_GetDevicesResponseIpcMsg_GetTimestamp(msg); ts=GWEN_Timestamp_fromInt64(ts64); /* TODO: fix timestamp */ fprintf(stdout, "- node: addr=%d, uid=0x%08x, device=", AQH_GetDevicesResponseIpcMsg_GetBusAddress(msg), (unsigned int) AQH_GetDevicesResponseIpcMsg_GetUid(msg)); u=AQH_GetDevicesResponseIpcMsg_GetManufacturer(msg); _printUintAsTextOrHex(u, 32); fprintf(stdout, ":"); u=AQH_GetDevicesResponseIpcMsg_GetDeviceType(msg); _printUintAsTextOrHex(u, 16); u=AQH_GetDevicesResponseIpcMsg_GetDeviceVersion(msg); fprintf(stdout, " v%d.%d", (u>>8) & 0xff, u & 0xff); u=AQH_GetDevicesResponseIpcMsg_GetFirmwareVersion(msg); fprintf(stdout, ", firmware=%d.%d.%d (%d), ", (u>>16) & 0xff, (u>>8) & 0xff, u & 0xff, (u>>24) & 0xff); if (ts) { fprintf(stdout, "last seen %s, ", GWEN_Timestamp_GetString(ts)); GWEN_Timestamp_free(ts); } fprintf(stdout, "pkg out: %d, pkg in: %d, collisions: %d, busy: %d, crc: %d, io: %d\n", AQH_GetDevicesResponseIpcMsg_GetPkgOut(msg), AQH_GetDevicesResponseIpcMsg_GetPkgIn(msg), AQH_GetDevicesResponseIpcMsg_GetCollisions(msg), AQH_GetDevicesResponseIpcMsg_GetBusy(msg), AQH_GetDevicesResponseIpcMsg_GetCrcErrors(msg), AQH_GetDevicesResponseIpcMsg_GetIoErrors(msg)); } void _printUintAsTextOrHex(uint32_t u, int bits) { int i; uint8_t d; int hasNonPrintable=0; int hasPrintable=0; for (i=0; i>i) & 0xff); if (d==0) { /* undecided */ } else if (isalnum(d)) hasPrintable=1; else hasNonPrintable=1; } if (hasNonPrintable || !hasPrintable) fprintf(stdout, "%08x", u); else { for (i=0; i>i) & 0xff); fprintf(stdout, "%c", d?d:' '); } } } int _sendGetDevices(GWEN_MSG_ENDPOINT *epTcp) { GWEN_MSG *msgOut; msgOut=AQH_GetDevicesRequestIpcMsg_new(AQH_MSGTYPE_IPC_NODES_GETDEVICES_REQ, GWEN_MsgEndpoint_GetNextMessageId(epTcp), 0); if (msgOut==NULL) { DBG_ERROR(NULL, "Error creating message"); return GWEN_ERROR_GENERIC; } GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); return 0; }