include the deviceClass/plugin name in all defines to avoid collisions between deviceClasses within the same file. So far this hasn't really been an issue because using idName we could set random ids. Now interfaces dictate the names, so having multiple deviceClasses in one file and both implementing the same interface would clash. This also should improve readability in the plugins code as we won't have ids like: "bridgeConnected" and "connected" available which easily causes the developer to accidentally use "connected" where instead "bridgeConnected" should be used (I actually found some bugs like this while updating plugins for this). The new style would force those states to be named like e.g. "bridgeConnected" and "lightConnected" which are not as easy to mix up.
439 lines
21 KiB
Python
Executable File
439 lines
21 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
# #
|
|
# Copyright (C) 2015-2017 Simon Stuerz <simon.stuerz@guh.io> #
|
|
# Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> #
|
|
# #
|
|
# This file is part of guh. #
|
|
# #
|
|
# guh 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. #
|
|
# #
|
|
# guh 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 guh. If not, see <http://www.gnu.org/licenses/>. #
|
|
# #
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
import argparse
|
|
import traceback
|
|
import json
|
|
import os
|
|
import subprocess
|
|
|
|
__version__='1.0.1'
|
|
|
|
##################################################################################################################
|
|
# Methods
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def printInfo(info):
|
|
if args.filetype is 'i':
|
|
print(info)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def printWarning(warning):
|
|
print('Warning: ' + warning)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def printError(error):
|
|
print('Error: ' + error)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def writeToFile(line):
|
|
outputFile.write('%s\n' % line)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractPlugin(pluginMap):
|
|
variableName = 'pluginId'
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define PluginId pluginId = %s' % (pluginMap['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('PluginId pluginId = PluginId(\"%s\");' % (pluginMap['id']))
|
|
addTranslationString(pluginMap['name'], 'The name of the plugin %s (%s)' % (pluginMap['name'], pluginMap['id']))
|
|
createExternDefinition('PluginId', variableName)
|
|
|
|
if 'paramTypes' in pluginMap:
|
|
extractParamTypes(pluginMap['paramTypes'], pluginMap['name'])
|
|
|
|
if 'vendors' in pluginMap:
|
|
extractVendors(pluginMap['vendors'])
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractParamTypes(paramTypes, contextName):
|
|
for paramType in paramTypes:
|
|
try:
|
|
variableName = '%sParamTypeId' % (contextName + paramType['name'][0].capitalize() + paramType['name'][1:])
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define ParamTypeId %s = %s' % (variableName, paramType['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('ParamTypeId %s = ParamTypeId(\"%s\");' % (variableName, paramType['id']))
|
|
addTranslationString(paramType['displayName'], 'The name of the paramType (%s) of %s' % (paramType['id'], contextName))
|
|
createExternDefinition('ParamTypeId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for ParamTypeId %s -> skipping' % (variableName, paramType['id']))
|
|
except:
|
|
pass
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractVendors(vendors):
|
|
for vendor in vendors:
|
|
try:
|
|
variableName = '%sVendorId' % (vendor['name'])
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define VendorId %s = %s' % (variableName, vendor['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('VendorId %s = VendorId(\"%s\");' % (variableName, vendor['id']))
|
|
addTranslationString(vendor['displayName'], 'The name of the vendor (%s)' % vendor['id'])
|
|
createExternDefinition('VendorId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for VendorId %s -> skipping' % (variableName, param['id']))
|
|
except:
|
|
pass
|
|
|
|
if 'deviceClasses' in vendor:
|
|
extractDeviceClasses(vendor['deviceClasses'])
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractDeviceClasses(deviceClasses):
|
|
for deviceClass in deviceClasses:
|
|
try:
|
|
variableName = '%sDeviceClassId' % (deviceClass['name'])
|
|
|
|
if 'pairingInfo' in deviceClass:
|
|
addTranslationString(deviceClass['pairingInfo'], 'The pairing info of deviceClass %s' % deviceClass['name'])
|
|
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define DeviceClassId %s = %s' % (variableName, deviceClass['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('DeviceClassId %s = DeviceClassId(\"%s\");' % (variableName, deviceClass['id']))
|
|
addTranslationString(deviceClass['displayName'], 'The name of the DeviceClass (%s)' %(deviceClass['id']))
|
|
createExternDefinition('DeviceClassId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for DeviceClassId %s -> skipping' % (variableName, deviceClass['deviceClassId']))
|
|
|
|
except:
|
|
pass
|
|
|
|
if 'paramTypes' in deviceClass:
|
|
extractParamTypes(deviceClass['paramTypes'], deviceClass['name'])
|
|
|
|
if 'discoveryParamTypes' in deviceClass:
|
|
extractParamTypes(deviceClass['discoveryParamTypes'], deviceClass['name'])
|
|
|
|
if 'stateTypes' in deviceClass:
|
|
extractStateTypes(deviceClass['stateTypes'], deviceClass['name'])
|
|
|
|
if 'actionTypes' in deviceClass:
|
|
extractActionTypes(deviceClass['actionTypes'], deviceClass['name'])
|
|
|
|
if 'eventTypes' in deviceClass:
|
|
extractEventTypes(deviceClass['eventTypes'], deviceClass['name'])
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractStateTypes(stateTypes, deviceClassName):
|
|
for stateType in stateTypes:
|
|
try:
|
|
# Define StateType
|
|
variableName = '%s%sStateTypeId' % (deviceClassName, stateType['name'][0].capitalize() + stateType['name'][1:])
|
|
#addTranslationString(stateType['name'], 'The name of the stateType (%s) of DeviceClass %s' % (stateType['id'], deviceClassName))
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define StateTypeId %s = %s' % (variableName, stateType['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('StateTypeId %s = StateTypeId(\"%s\");' % (variableName, stateType['id']))
|
|
createExternDefinition('StateTypeId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for StateTypeId %s -> skipping' % (variableName, stateType['id']))
|
|
|
|
# Create EventTypeId for this state
|
|
variableName = '%s%sEventTypeId' % (deviceClassName, stateType['name'][0].capitalize() + stateType['name'][1:])
|
|
if not variableName in variableNames:
|
|
addTranslationString(stateType['displayNameEvent'], 'The name of the autocreated EventType (%s)' % stateType['id'])
|
|
variableNames.append(variableName)
|
|
printInfo('Define EventTypeId %s = %s' % (variableName, stateType['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('EventTypeId %s = EventTypeId(\"%s\");' % (variableName, stateType['id']))
|
|
createExternDefinition('EventTypeId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for autocreated EventTypeId %s -> skipping' % (variableName, stateType['id']))
|
|
|
|
#ParamType for EventType/ActionType
|
|
variableName = '%s%sStateParamTypeId' % (deviceClassName, stateType['name'][0].capitalize() + stateType['name'][1:])
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define ParamTypeId %s for StateType %s = %s' % (variableName, variableName, stateType['id']))
|
|
if args.filetype is 'i':
|
|
writeToFile('ParamTypeId %s = ParamTypeId(\"%s\");' % (variableName, stateType['id']))
|
|
createExternDefinition('ParamTypeId', variableName)
|
|
addTranslationString(stateType['name'], 'The name of the ParamType of StateType (%s) of DeviceClass %s' % (stateType['id'], deviceClassName))
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for ParamTypeId %s -> skipping' % (variableName, stateType['id']))
|
|
|
|
# Create ActionTypeId if the state is writable
|
|
if 'writable' in stateType and stateType['writable']:
|
|
variableName = '%s%sActionTypeId' % (deviceClassName, stateType['name'][0].capitalize() + stateType['name'][1:])
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
printInfo('Define ActionTypeId for writable StateType %s = %s' % (variableName, stateType['id']))
|
|
addTranslationString(stateType['displayNameAction'], 'The name of the autocreated ActionType (%s)' % stateType['id'])
|
|
if args.filetype is 'i':
|
|
writeToFile('ActionTypeId %s = ActionTypeId(\"%s\");' % (variableName, stateType['id']))
|
|
createExternDefinition('ActionTypeId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for autocreated ActionTypeId %s -> skipping' % (variableName, stateType['id']))
|
|
|
|
except:
|
|
pass
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractActionTypes(actionTypes, deviceClassName):
|
|
for actionType in actionTypes:
|
|
try:
|
|
# Define ActionTypeId
|
|
variableName = '%s%sActionTypeId' % (deviceClassName, actionType['name'][0].capitalize() + actionType['name'][1:])
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
addTranslationString(actionType['displayName'], 'The name of the ActionType %s of deviceClass %s' % (actionType['id'], deviceClassName))
|
|
if args.filetype is 'i':
|
|
writeToFile('ActionTypeId %s = ActionTypeId(\"%s\");' % (variableName, actionType['id']))
|
|
createExternDefinition('ActionTypeId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for ActionTypeId %s -> skipping' % (variableName, actionType['id']))
|
|
|
|
except:
|
|
pass
|
|
|
|
# Define paramTypes of this ActionType
|
|
if 'paramTypes' in actionType:
|
|
extractParamTypes(actionType['paramTypes'], deviceClassName)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def extractEventTypes(eventTypes, deviceClassName):
|
|
for eventType in eventTypes:
|
|
try:
|
|
# Define EventTypeId
|
|
variableName = '%s%sEventTypeId' % (deviceClassName, eventType['name'][0].capitalize() + eventType['name'][1:])
|
|
if not variableName in variableNames:
|
|
variableNames.append(variableName)
|
|
addTranslationString(eventType['displayName'], 'The name of the EventType %s of deviceClass %s' % (eventType['id'], deviceClassName))
|
|
if args.filetype is 'i':
|
|
writeToFile('EventTypeId %s = EventTypeId(\"%s\");' % (variableName, eventType['id']))
|
|
createExternDefinition('EventTypeId', variableName)
|
|
else:
|
|
printWarning('Duplicated variable name \"%s\" for EventTypeId %s -> skipping' % (variableName, eventType['id']))
|
|
except:
|
|
pass
|
|
|
|
# Define paramTypes of this EventType
|
|
if 'paramTypes' in eventType:
|
|
extractParamTypes(eventType['paramTypes'], deviceClassName)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def createExternDefinition(type, name):
|
|
definition = {}
|
|
definition['type'] = type
|
|
definition['variable'] = name
|
|
externDefinitions.append(definition)
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def addTranslationString(string, comment):
|
|
translationStrings.append([string, comment])
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def writeTranslationStrings():
|
|
if len(translationStrings) is not 0:
|
|
writeToFile('// Translation strings')
|
|
writeToFile('const QString translations[] {')
|
|
|
|
for index, value in enumerate(translationStrings):
|
|
writeToFile(' //: %s' % value[1])
|
|
if index != len(translationStrings) - 1:
|
|
writeToFile(' QT_TRANSLATE_NOOP(\"%s\", \"%s\"), \n' % (pluginMap['name'], value[0]))
|
|
else:
|
|
writeToFile(' QT_TRANSLATE_NOOP(\"%s\", \"%s\")' % (pluginMap['name'], value[0]))
|
|
|
|
writeToFile('};')
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def createTranslationFiles():
|
|
for translation in args.translations:
|
|
translationFile = (sourceDir + '/' + translation)
|
|
path, fileName = os.path.split(translationFile)
|
|
translationOutput = (path + '/' + pluginMap['id'] + '-' + os.path.splitext(fileName)[0] + '.qm')
|
|
printInfo(' --> Translation update %s' % translationFile)
|
|
printInfo(subprocess.check_output(['mkdir', '-p', path]))
|
|
printInfo(subprocess.check_output(['lupdate', '-recursive', '-no-obsolete', sourceDir, (args.builddir + '/' + args.output), '-ts', translationFile]))
|
|
printInfo(' --> Translation release %s' % translationOutput)
|
|
printInfo(subprocess.check_output(['lrelease', translationFile, '-qm', translationOutput]))
|
|
printInfo(' --> Copy translation files to build dir %s' % args.builddir + '/translations/')
|
|
subprocess.check_output(['rsync', '-a', translationOutput, args.builddir + '/translations/'])
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def writePluginInfoFile():
|
|
print(' --> Generate plugininfo.h for plugin \"%s\" = %s' % (pluginMap['name'], pluginMap['id']))
|
|
|
|
writeToFile('/* This file is generated by the guh build system. Any changes to this file will')
|
|
writeToFile(' * be lost.')
|
|
writeToFile(' *')
|
|
writeToFile(' * If you want to change this file, edit the plugin\'s json file.')
|
|
writeToFile(' */')
|
|
writeToFile('')
|
|
writeToFile('#ifndef PLUGININFO_H')
|
|
writeToFile('#define PLUGININFO_H')
|
|
writeToFile('')
|
|
writeToFile('#include <QLoggingCategory>')
|
|
writeToFile('#include <QObject>')
|
|
writeToFile('')
|
|
writeToFile('#include \"typeutils.h\"')
|
|
writeToFile('')
|
|
writeToFile('// Id definitions')
|
|
extractPlugin(pluginMap)
|
|
writeToFile('')
|
|
writeToFile('// Logging category')
|
|
|
|
if 'name' in pluginMap:
|
|
debugCategoryName = pluginMap['name'][0].capitalize() + pluginMap['name'][1:]
|
|
writeToFile('Q_DECLARE_LOGGING_CATEGORY(dc%s)' % debugCategoryName)
|
|
writeToFile('Q_LOGGING_CATEGORY(dc%s, \"%s\")' % (debugCategoryName, debugCategoryName))
|
|
printInfo('Define logging category: \'dc%s\'' % debugCategoryName)
|
|
|
|
writeToFile('')
|
|
|
|
# Write translation strings
|
|
writeTranslationStrings()
|
|
|
|
writeToFile('')
|
|
writeToFile('#endif // PLUGININFO_H')
|
|
outputFile.close()
|
|
print(' --> Generated successfully \"%s\"' % (args.output))
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------
|
|
def writeExternPluginInfoFile():
|
|
print(' --> Generate extern-plugininfo.h for plugin \"%s\" = %s' % (pluginMap['name'], pluginMap['id']))
|
|
extractPlugin(pluginMap)
|
|
writeToFile('/* This file is generated by the guh build system. Any changes to this file will')
|
|
writeToFile(' * be lost.')
|
|
writeToFile(' *')
|
|
writeToFile(' * If you want to change this file, edit the plugin\'s json file.')
|
|
writeToFile(' */')
|
|
writeToFile('')
|
|
writeToFile('#ifndef EXTERNPLUGININFO_H')
|
|
writeToFile('#define EXTERNPLUGININFO_H')
|
|
writeToFile('#include \"typeutils.h\"')
|
|
writeToFile('#include <QLoggingCategory>')
|
|
writeToFile('')
|
|
writeToFile('// Id definitions')
|
|
|
|
for externDefinition in externDefinitions:
|
|
writeToFile('extern %s %s;' % (externDefinition['type'], externDefinition['variable']))
|
|
|
|
writeToFile('')
|
|
writeToFile('// Logging category definition')
|
|
|
|
if 'name' in pluginMap:
|
|
debugCategoryName = pluginMap['name'][0].capitalize() + pluginMap['name'][1:]
|
|
writeToFile('Q_DECLARE_LOGGING_CATEGORY(dc%s)' % debugCategoryName)
|
|
|
|
writeToFile('')
|
|
writeToFile('#endif // EXTERNPLUGININFO_H')
|
|
outputFile.close()
|
|
print(' --> Generated successfully \'%s\'' % (args.output))
|
|
|
|
|
|
##################################################################################################################
|
|
# Main
|
|
##################################################################################################################
|
|
|
|
if __name__ == '__main__':
|
|
# Argument parser
|
|
parser = argparse.ArgumentParser(description='The guh-generateplugininfo is a precompiler for building plugins. This precompiler will create a plugininfo.h containing the uuid definitions from the plugin json file and creates the translations for the plugin.')
|
|
parser.add_argument('-j', '--jsonfile', help='The JSON input file name with the plugin description', metavar='jsonfile', required=True)
|
|
parser.add_argument('-b', '--builddir', help='The path to the build directory of the plugin where the plugininfo.h file can be found.', metavar='buildpath', required=True)
|
|
parser.add_argument('-f', '--filetype', help='The file type to generate: e = extern infofile, i = infofile', action='store', choices=['e', 'i'], default='i')
|
|
parser.add_argument('-o', '--output', help='The plugininfo.h outputFile with the uuid declarations', metavar='output')
|
|
parser.add_argument('-t', '--translations', help='The translation files for the plugin.', nargs='*', type=str, metavar='*.ts')
|
|
parser.add_argument('-v', '--version', action='version', version=__version__)
|
|
args = parser.parse_args()
|
|
|
|
# Get the source directors
|
|
sourceDir = os.path.dirname(os.path.abspath(args.jsonfile))
|
|
|
|
# Print build information for debugging
|
|
printInfo('Json file: %s' % args.jsonfile)
|
|
printInfo('Output: %s/%s' % (args.builddir, args.output))
|
|
printInfo('Build directory: %s' % args.builddir)
|
|
printInfo('Source directory: %s' % sourceDir)
|
|
printInfo('Translations: %s' % args.translations)
|
|
printInfo('FileType: %s' % args.filetype)
|
|
|
|
# Tuple ('string to translate', 'comment for translater')
|
|
translationStrings = []
|
|
|
|
variableNames = []
|
|
externDefinitions = []
|
|
|
|
# Open files
|
|
try:
|
|
inputFile = open(args.jsonfile, 'r')
|
|
except:
|
|
printError('Could not open file \"%s\"' % (args.jsonfile))
|
|
exit -1
|
|
|
|
try:
|
|
outputFile = open(args.builddir + '/' + args.output, 'w')
|
|
except:
|
|
printError('Could not open file \"%s\"' % (args.jsonfile))
|
|
exit -1
|
|
|
|
# Read json file
|
|
try:
|
|
pluginMap = json.loads(inputFile.read())
|
|
inputFile.close()
|
|
except ValueError as error:
|
|
printError(' --> Could not load json input file \"%s\"' % (args.input))
|
|
printError(' %s' % (error))
|
|
inputFile.close()
|
|
exit -1
|
|
|
|
# Write files
|
|
if args.filetype is 'i':
|
|
writePluginInfoFile()
|
|
else:
|
|
writeExternPluginInfoFile()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|