From 6f1e22546a901e32ba8d0cdb691c5b22c66887bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 5 Dec 2018 14:54:40 +0100 Subject: [PATCH] Replace generate interface documentation script and improve interfaces docs --- doc/generate-interfaces-qdoc.py | 216 +++++++++++++++++++++++++++++ doc/interfaces.qdoc | 4 - doc/jsonrpc-api.qdoc | 109 +++++++++++++-- libnymea/interfaces/generatedoc.sh | 16 --- nymea.pro | 2 +- 5 files changed, 312 insertions(+), 35 deletions(-) create mode 100755 doc/generate-interfaces-qdoc.py delete mode 100755 libnymea/interfaces/generatedoc.sh diff --git a/doc/generate-interfaces-qdoc.py b/doc/generate-interfaces-qdoc.py new file mode 100755 index 00000000..625d1b57 --- /dev/null +++ b/doc/generate-interfaces-qdoc.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python + +# -*- coding: UTF-8 -*- + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # +# Copyright (C) 2018 Simon Stuerz # +# # +# This file is part of nymea. # +# # +# nymea is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, version 2 of the License. # +# # +# nymea is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with nymea. If not, see . # +# # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +import argparse +import traceback +import json +import os +import sys +import subprocess + +__version__='1.0.0' + + +#-------------------------------------------------------------------------- +def printInfo(info): + print('[+] ' + info) + + +#-------------------------------------------------------------------------- +def printWarning(warning): + print('[-] Warning: ' + warning) + + +#-------------------------------------------------------------------------- +def printError(error): + print('[!] Error: ' + error) + + +#-------------------------------------------------------------------------- +def writeToFile(line): + outputFile.write('%s\n' % line) + + +#-------------------------------------------------------------------------- +def writeCodeSection(jsonData): + writeToFile('\code') + writeToFile(json.dumps(jsonData, sort_keys=True, indent=4)) + writeToFile('\endcode') + writeToFile('') + + +#-------------------------------------------------------------------------- +def loadInterfaces(): + printInfo('Loading interfaces files from %s' % interfacesDirectory) + + interfaces = {} + interfaceFiles = [] + + # Read the file list + for fileName in os.listdir(interfacesDirectory): + if ".json" in fileName: + interfaceFiles.append(interfacesDirectory + "/" + fileName) + + # Sort file lists for beeing able to get the last n days logs + interfaceFiles.sort() + for fileName in interfaceFiles: + name = os.path.basename(fileName) + interfaceName = os.path.splitext(name)[0] + #printInfo(' %s --> %s | %s' % (interfaceName, name, fileName)) + interfaces[interfaceName] = loadJsonData(fileName) + + return interfaces + + +#-------------------------------------------------------------------------- +def loadJsonData(fileName): + # Open the file + try: + jsonFile = open(fileName, 'r') + except: + printError('Could not open JSON file \"%s\"' % (fileName)) + exit(-1) + + jsonFileContent = jsonFile.read() + jsonFile.close() + + # Parse json content + try: + data = json.loads(jsonFileContent) + except ValueError as error: + printError('Could not load json content from %s' % (fileName)) + printError(' %s' % (error)) + exit(-1) + + return data + + +#-------------------------------------------------------------------------- +def writeDocumentationContent(): + printInfo('Write interfaces documentation content to %s' % outputFileName) + + writeToFile('\section1 Available interfaces') + writeToFile('This following list shows you the current available interfaces.') + writeToFile('') + + # Create the interfaces list + writeToFile('\list') + for interfaceName in interfaceNames: + writeToFile(' \li \l{%s}' % interfaceName) + + writeToFile('\endlist') + writeToFile('') + writeToFile('') + + # Extract interface information + writeInterfaces() + + + +#-------------------------------------------------------------------------- +def writeInterfaces(): + for interfaceName in interfaceNames: + writeToFile('\section2 %s' % interfaceName) + interfaceJson = interfaces[interfaceName] + # If a desciption is provided, use it and remove it from the map + if 'description' in interfaceJson: + writeToFile('%s' % interfaceJson['description'] ) + interfaceJson.pop('description') + + writeCodeSection(interfaceJson) + writeToFile('') + + writeExtends(interfaceJson) + writeToFile('') + + + + +#-------------------------------------------------------------------------- +def writeExtends(interfaceJson): + if 'extends' in interfaceJson: + if type(interfaceJson['extends']) is list: + #printInfo('extends is list: %s' % interfaceJson['extends']) + extendsList = list(interfaceJson['extends']) + extendsString = "See also: " + + extendsCount = len(extendsList) + for i in range(len(extendsList)): + if i is extendsCount - 1: + extendsString += '\l{%s}' % extendsList[i] + else: + extendsString += '\l{%s}, ' % extendsList[i] + + writeToFile(extendsString) + else: + #printInfo('extends is string: %s' % interfaceJson['extends']) + writeToFile('See also: \l{%s}' % interfaceJson['extends']) + + + +########################################################################### +# Main +########################################################################### + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='This tool generates a qdoc file out of the interfaces JSON files for the online documentation.') + parser.add_argument('-v', '--version', action='version', version=__version__) + parser.add_argument('-i', '--interfaces', help='The path to the interfaces JSON files. Default is ../libnymea/interfaces/', metavar='path', default='../libnymea/interfaces/') + parser.add_argument('-o', '--output', help='The qdoc output file with the generated documentation script. Default is interfacelist.qdoc', metavar='output', default='interfacelist.qdoc') + args = parser.parse_args() + + interfacesDirectory = os.path.abspath(args.interfaces) + outputFileName = os.path.dirname(os.path.realpath(sys.argv[0])) + "/" + args.output + + # Print build information for debugging + printInfo('--> Interfaces directory: %s' % interfacesDirectory) + printInfo('--> Output: %s' % outputFileName) + + # Verify interfaces path + if not os.path.isdir(interfacesDirectory): + printError('The given interfaces directory path does not exist \"%s\"' % (interfacesDirectory)) + exit(-1) + + + # Open qdoc file for writing + try: + outputFile = open(outputFileName, 'w') + except: + printError('Could not open output file \"%s\"' % (outputFileName)) + exit(-1) + + + # Load all interface files + interfaces = loadInterfaces() + + # Get a alphabetic sorted list of interfaces + interfaceNames = interfaces.keys() + interfaceNames.sort() + + # Write the documentation + writeDocumentationContent() + + outputFile.close() + printInfo('Done.') + diff --git a/doc/interfaces.qdoc b/doc/interfaces.qdoc index b783e104..8ec359bc 100644 --- a/doc/interfaces.qdoc +++ b/doc/interfaces.qdoc @@ -29,10 +29,6 @@ A dimmablelight extends this type and adds a brightness property to it. This means, if a DeviceClass implements \l {dimmablelight}, it also needs to cater for the \l {light} interface's states. - - \section1 Available interfaces - This following list shows you the current available interfaces. - \include interfacelist.qdoc */ diff --git a/doc/jsonrpc-api.qdoc b/doc/jsonrpc-api.qdoc index 8299b9e5..4bb429ea 100644 --- a/doc/jsonrpc-api.qdoc +++ b/doc/jsonrpc-api.qdoc @@ -1,5 +1,5 @@ /*! -In the following section you can find a detaild description of the current API version 1.7. +In the following section you can find a detaild description of the current API version 1.10. \list \li \l{Types} \li \l{Methods} @@ -78,6 +78,15 @@ See also: \l{ParamType} } \endcode See also: \l{RepeatingOption} +\section2 CloudConnectionState +\code +[ + "CloudConnectionStateDisabled", + "CloudConnectionStateUnconfigured", + "CloudConnectionStateConnecting", + "CloudConnectionStateConnected" +] +\endcode \section2 ConfigurationError \code [ @@ -644,7 +653,12 @@ See also: \l{Unit}, \l{BasicType} \section2 TagError \code -"$ref:TagError" +[ + "TagErrorNoError", + "TagErrorDeviceNotFound", + "TagErrorRuleNotFound", + "TagErrorTagNotFound" +] \endcode \section2 TimeDescriptor \code @@ -729,7 +743,9 @@ See also: \l{RepeatingOption} "UnitMilliVolt", "UnitVoltAmpere", "UnitVoltAmpereReactive", - "UnitAmpereHour" + "UnitAmpereHour", + "UnitMicroSiemensPerCentimeter", + "UnitDuration" ] \endcode \section2 UserError @@ -1492,12 +1508,13 @@ Returns \code { "methods": "Object", + "notifications": "Object", "types": "Object" } \endcode \section2 JSONRPC.IsCloudConnected -Check whether the cloud is currently connected. +Check whether the cloud is currently connected. "connected" will be true whenever connectionState equals CloudConnectionStateConnected and is deprecated. Please use the connectionState value instead. Params \code {} @@ -1505,10 +1522,11 @@ Params Returns \code { - "connected": "Bool" + "connected": "Bool", + "connectionState": "$ref:CloudConnectionState" } \endcode - +See also: \l{CloudConnectionState} \section2 JSONRPC.KeepAlive Keep alive a remote connection. The sessionId is the MQTT topic which has been used to establish the session. It will return false if no ongoing session with the given ID can be found. Params @@ -1570,6 +1588,25 @@ Returns } \endcode +\section2 JSONRPC.SetupCloudConnection +Sets up the cloud connection by deploying a certificate and its configuration. +Params +\code +{ + "certificatePEM": "String", + "endpoint": "String", + "privateKey": "String", + "publicKey": "String", + "rootCA": "String" +} +\endcode +Returns +\code +{ + "success": "Bool" +} +\endcode + \section2 JSONRPC.SetupRemoteAccess Setup the remote connection by providing AWS token information. This requires the cloud to be connected. Params @@ -1617,7 +1654,15 @@ Returns \endcode \section2 Logging.GetLogEntries -Get the LogEntries matching the given filter. Each list element of a given filter will be connected with OR to each other. Each of the given filters will be connected with AND to each other. +Get the LogEntries matching the given filter. The result set will contain entries matching all filter rules combined. If multiple options are given for a single filter type, the result set will contain entries matching any of those. The offset starts at the newest entry in the result set. By default all items are returned. Example: If the specified filter returns a total amount of 100 entries: +- a offset value of 10 would include the oldest 90 entries +- a offset value of 0 would return all 100 entries + +The offset is particularly useful in combination with the maxCount property and can be used for pagination. E.g. A result set of 10000 entries can be fetched in batches of 1000 entries by fetching +1) offset 0, maxCount 1000: Entries 0 to 9999 +2) offset 10000, maxCount 1000: Entries 10000 - 19999 +3) offset 20000, maxCount 1000: Entries 20000 - 29999 +... Params \code { @@ -1627,12 +1672,14 @@ Params "o:eventTypes": [ "$ref:LoggingEventType" ], + "o:limit": "Int", "o:loggingLevels": [ "$ref:LoggingLevel" ], "o:loggingSources": [ "$ref:LoggingSource" ], + "o:offset": "Int", "o:timeFilters": [ { "o:endDate": "Int", @@ -1650,10 +1697,12 @@ Params Returns \code { + "count": "Int", "loggingError": "$ref:LoggingError", "o:logEntries": [ "$ref:LogEntry" - ] + ], + "offset": "Int" } \endcode See also: \l{LogEntry}, \l{LoggingError}, \l{LoggingLevel}, \l{LoggingEventType}, \l{LoggingSource} @@ -2768,14 +2817,16 @@ See also: \l{Tag} "params": {}, "returns": { "methods": "Object", + "notifications": "Object", "types": "Object" } }, "JSONRPC.IsCloudConnected": { - "description": "Check whether the cloud is currently connected.", + "description": "Check whether the cloud is currently connected. \"connected\" will be true whenever connectionState equals CloudConnectionStateConnected and is deprecated. Please use the connectionState value instead.", "params": {}, "returns": { - "connected": "Bool" + "connected": "Bool", + "connectionState": "$ref:CloudConnectionState" } }, "JSONRPC.KeepAlive": { @@ -2815,6 +2866,19 @@ See also: \l{Tag} "enabled": "Bool" } }, + "JSONRPC.SetupCloudConnection": { + "description": "Sets up the cloud connection by deploying a certificate and its configuration.", + "params": { + "certificatePEM": "String", + "endpoint": "String", + "privateKey": "String", + "publicKey": "String", + "rootCA": "String" + }, + "returns": { + "success": "Bool" + } + }, "JSONRPC.SetupRemoteAccess": { "description": "Setup the remote connection by providing AWS token information. This requires the cloud to be connected.", "params": { @@ -2844,7 +2908,7 @@ See also: \l{Tag} } }, "Logging.GetLogEntries": { - "description": "Get the LogEntries matching the given filter. Each list element of a given filter will be connected with OR to each other. Each of the given filters will be connected with AND to each other.", + "description": "Get the LogEntries matching the given filter. The result set will contain entries matching all filter rules combined. If multiple options are given for a single filter type, the result set will contain entries matching any of those. The offset starts at the newest entry in the result set. By default all items are returned. Example: If the specified filter returns a total amount of 100 entries:\n- a offset value of 10 would include the oldest 90 entries\n- a offset value of 0 would return all 100 entries\n\nThe offset is particularly useful in combination with the maxCount property and can be used for pagination. E.g. A result set of 10000 entries can be fetched in batches of 1000 entries by fetching\n1) offset 0, maxCount 1000: Entries 0 to 9999\n2) offset 10000, maxCount 1000: Entries 10000 - 19999\n3) offset 20000, maxCount 1000: Entries 20000 - 29999\n...", "params": { "o:deviceIds": [ "Uuid" @@ -2852,12 +2916,14 @@ See also: \l{Tag} "o:eventTypes": [ "$ref:LoggingEventType" ], + "o:limit": "Int", "o:loggingLevels": [ "$ref:LoggingLevel" ], "o:loggingSources": [ "$ref:LoggingSource" ], + "o:offset": "Int", "o:timeFilters": [ { "o:endDate": "Int", @@ -2872,10 +2938,12 @@ See also: \l{Tag} ] }, "returns": { + "count": "Int", "loggingError": "$ref:LoggingError", "o:logEntries": [ "$ref:LogEntry" - ] + ], + "offset": "Int" } }, "NetworkManager.ConnectWifiNetwork": { @@ -3373,6 +3441,12 @@ See also: \l{Tag} "o:repeating": "$ref:RepeatingOption", "o:startTime": "Time" }, + "CloudConnectionState": [ + "CloudConnectionStateDisabled", + "CloudConnectionStateUnconfigured", + "CloudConnectionStateConnecting", + "CloudConnectionStateConnected" + ], "ConfigurationError": [ "ConfigurationErrorNoError", "ConfigurationErrorInvalidTimeZone", @@ -3798,7 +3872,12 @@ See also: \l{Tag} "o:value": "String", "tagId": "String" }, - "TagError": "$ref:TagError", + "TagError": [ + "TagErrorNoError", + "TagErrorDeviceNotFound", + "TagErrorRuleNotFound", + "TagErrorTagNotFound" + ], "TimeDescriptor": { "o:calendarItems": [ "$ref:CalendarItem" @@ -3868,7 +3947,9 @@ See also: \l{Tag} "UnitMilliVolt", "UnitVoltAmpere", "UnitVoltAmpereReactive", - "UnitAmpereHour" + "UnitAmpereHour", + "UnitMicroSiemensPerCentimeter", + "UnitDuration" ], "UserError": [ "UserErrorNoError", diff --git a/libnymea/interfaces/generatedoc.sh b/libnymea/interfaces/generatedoc.sh deleted file mode 100755 index f3eddbb7..00000000 --- a/libnymea/interfaces/generatedoc.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -out_file=../../doc/interfacelist.qdoc -cur_dir=`pwd` -echo > $out_file -echo "\\list" >> $out_file -for i in `ls *.json`; do - echo "\\li \l{$i}" | sed s/\.json// >> $out_file -done -echo "\\endlist" >> $out_file - -for i in `ls *.json`; do - echo "\\\target $i" | sed s/\.json// >> $out_file - echo "\\section2 $i" | sed s/\.json// >> $out_file - echo "\\quotefile $cur_dir/$i" >> $out_file -done diff --git a/nymea.pro b/nymea.pro index fb87d4e9..ccb07bda 100644 --- a/nymea.pro +++ b/nymea.pro @@ -11,7 +11,7 @@ tests.depends = libnymea libnymea-core doc.depends = FORCE # Note: some how extraimages in qdocconf did not the trick -doc.commands += cd $$top_srcdir/libnymea/interfaces; ./generatedoc.sh; +doc.commands += cd $$top_srcdir/doc; ./generate-interfaces-qdoc.py; doc.commands += cd $$top_srcdir/doc; ./generate-api-qdoc.py; doc.commands += cd $$top_srcdir/doc; qdoc --highlighting config.qdocconf; cp -r images/* html/images/; \ cp -r favicons/* html/; cp -r $$top_srcdir/doc/html $$top_builddir/