mirror of
https://github.com/thingsboard/thingsboard-gateway
synced 2025-10-26 22:31:42 +08:00
Added setup wizard and updated setup.py
This commit is contained in:
5
setup.py
5
setup.py
@@ -54,7 +54,10 @@ setup(
|
||||
'paho-mqtt',
|
||||
'PyYAML',
|
||||
'simplejson',
|
||||
'requests'
|
||||
'requests',
|
||||
'PyInquirer',
|
||||
'pyfiglet',
|
||||
'termcolor'
|
||||
],
|
||||
download_url='https://github.com/thingsboard/thingsboard-gateway/archive/%s.tar.gz' % VERSION,
|
||||
entry_points={
|
||||
|
||||
342
thingsboard_gateway/gateway/setup-wizard.py
Normal file
342
thingsboard_gateway/gateway/setup-wizard.py
Normal file
@@ -0,0 +1,342 @@
|
||||
from __future__ import print_function, unicode_literals
|
||||
from PyInquirer import style_from_dict, Token, prompt
|
||||
from pyfiglet import Figlet
|
||||
from termcolor import colored
|
||||
from prompt_toolkit.validation import ValidationError, Validator
|
||||
from re import compile, IGNORECASE
|
||||
from os.path import exists, splitext
|
||||
from yaml import safe_load, dump
|
||||
|
||||
style = style_from_dict({
|
||||
Token.Separator: '#cc5454',
|
||||
Token.QuestionMark: '#673ab7 bold',
|
||||
Token.Selected: '#673ab7',
|
||||
Token.Pointer: '#673ab7 bold',
|
||||
Token.Instruction: '', # default
|
||||
Token.Answer: '#673ab7 bold',
|
||||
Token.Question: '',
|
||||
})
|
||||
|
||||
|
||||
class NotNullValidator(Validator):
|
||||
def validate(self, document):
|
||||
if not document.text or document.text == '':
|
||||
raise ValidationError(message='Value can be empty!', cursor_position=len(document.text))
|
||||
|
||||
|
||||
class NumberValidator(Validator):
|
||||
def validate(self, document):
|
||||
try:
|
||||
int(document.text)
|
||||
except ValueError:
|
||||
raise ValidationError(message='Must be a number type!', cursor_position=len(document.text))
|
||||
|
||||
|
||||
class PortValidator(Validator):
|
||||
def validate(self, document):
|
||||
try:
|
||||
port = int(document.text)
|
||||
except ValueError:
|
||||
raise ValidationError(message='Must be a number type!', cursor_position=len(document.text))
|
||||
|
||||
if not 1 <= port <= 65535:
|
||||
raise ValidationError(message='Port is invalid!', cursor_position=len(document.text))
|
||||
|
||||
|
||||
class HostValidator(NotNullValidator):
|
||||
def validate(self, document):
|
||||
super(HostValidator, self).validate(document)
|
||||
|
||||
hostname = document.text
|
||||
if len(hostname) > 255:
|
||||
return ValidationError(message='Host is invalid!', cursor_position=len(document.text))
|
||||
|
||||
if hostname[-1] == ".":
|
||||
hostname = hostname[:-1]
|
||||
|
||||
allowed = compile(r"(?!-)[A-Z\d-]{1,63}(?<!-)$", IGNORECASE)
|
||||
if not all(allowed.match(x) for x in hostname.split(".")):
|
||||
raise ValidationError(message='Host is invalid!', cursor_position=len(document.text))
|
||||
|
||||
|
||||
class PathValidator(Validator):
|
||||
def validate(self, document):
|
||||
if not exists(document.text):
|
||||
raise ValidationError(message='File doesn\'t exist!', cursor_position=len(document.text))
|
||||
|
||||
if splitext(document.text)[1] != '.pem':
|
||||
raise ValidationError(message='File must be .pem extension!', cursor_position=len(document.text))
|
||||
|
||||
|
||||
def generate_config_file(data: {str: str}) -> None:
|
||||
print(data)
|
||||
with open('thingsboard_gateway/config/tb_gateway.yaml', 'w') as file:
|
||||
dump(data, file, default_flow_style=False, sort_keys=False)
|
||||
|
||||
|
||||
def read_config_file() -> {str: str}:
|
||||
with open('thingsboard_gateway/config/tb_gateway.yaml', 'r') as file:
|
||||
config_dict = safe_load(file)
|
||||
|
||||
return config_dict
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
default_config = read_config_file()
|
||||
|
||||
base_questions = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'host',
|
||||
'message': 'Thingsboard host:',
|
||||
'default': default_config['thingsboard']['host'],
|
||||
'validate': HostValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'port',
|
||||
'message': 'Thingsboard port:',
|
||||
'default': str(default_config['thingsboard']['port']),
|
||||
'validate': PortValidator
|
||||
},
|
||||
{
|
||||
'type': 'confirm',
|
||||
'name': 'remoteShell',
|
||||
'message': 'Do you want to have access from remote shell?',
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'type': 'confirm',
|
||||
'name': 'remoteConfiguration',
|
||||
'message': 'Do you want to have access for remote configuration?',
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'statsSendPeriodInSeconds',
|
||||
'message': 'Statistics will be send every (sec.):',
|
||||
'default': str(default_config['thingsboard']['statsSendPeriodInSeconds']),
|
||||
'validate': NumberValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'checkConnectorsConfigurationInSeconds',
|
||||
'message': 'Connectors config files will check every (sec.):',
|
||||
'default': str(default_config['thingsboard']['checkConnectorsConfigurationInSeconds']),
|
||||
'validate': NumberValidator
|
||||
},
|
||||
{
|
||||
'type': 'list',
|
||||
'name': 'security',
|
||||
'message': 'What security type do you need?',
|
||||
'choices': [
|
||||
'Access Token (Basic Security)',
|
||||
'TLS + Access Token (Advanced Security)',
|
||||
'TLS + Private Key (Advanced Security)'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
f = Figlet(font='slant')
|
||||
print(colored(f.renderText('TB IoT Gateway'), color='blue'))
|
||||
print(colored('Welcome to ThingsBoard IoT Gateway Setup Wizard', 'blue'))
|
||||
print(colored('Let\'s configure you Gateway by answering on questions below ⬇\n'))
|
||||
|
||||
base_answers = prompt(base_questions, style=style)
|
||||
|
||||
access_token_config = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'accessToken',
|
||||
'message': 'Your token:',
|
||||
'validate': NotNullValidator
|
||||
}
|
||||
]
|
||||
tls = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'caCert',
|
||||
'message': 'Path to your CA file (.pem):',
|
||||
'validate': PathValidator
|
||||
}
|
||||
]
|
||||
tls_access_token_config = access_token_config + tls
|
||||
tls_private_key_config = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'privateKey',
|
||||
'message': 'Path to you private key file (.pem):',
|
||||
'validate': PathValidator
|
||||
}
|
||||
] + tls + [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'cert',
|
||||
'message': 'Path to your certificate file (.pem):',
|
||||
'validate': PathValidator
|
||||
}
|
||||
]
|
||||
|
||||
if base_answers['security'] == 'Access Token (Basic Security)':
|
||||
security_questions = access_token_config
|
||||
elif base_answers['security'] == 'TLS + Access Token (Advanced Security)':
|
||||
security_questions = tls_access_token_config
|
||||
else:
|
||||
security_questions = tls_private_key_config
|
||||
|
||||
security_answers = prompt(security_questions, style=style)
|
||||
|
||||
qos_and_storage_type_question = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'qos',
|
||||
'message': 'QoS:',
|
||||
'validate': NumberValidator,
|
||||
'default': str(default_config['thingsboard']['qos']),
|
||||
},
|
||||
{
|
||||
'type': 'list',
|
||||
'name': 'storage',
|
||||
'message': 'Choose storage type:',
|
||||
'choices': [
|
||||
'Memory',
|
||||
'File storage'
|
||||
],
|
||||
'filter': lambda val: val.lower() if val == 'Memory' else 'file'
|
||||
}
|
||||
]
|
||||
|
||||
qos_and_storage_type_answers = prompt(qos_and_storage_type_question, style=style)
|
||||
|
||||
if qos_and_storage_type_answers['storage'] == 'memory':
|
||||
storage_questions = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'read_records_count',
|
||||
'message': 'Count of messages to get from storage and send to ThingsBoard:',
|
||||
'default': str(default_config['storage'].get('read_records_count', '')),
|
||||
'validate': NumberValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'max_records_count',
|
||||
'message': 'Maximum count of data in storage before send to ThingsBoard:',
|
||||
'default': str(default_config['storage'].get('max_records_count', '')),
|
||||
'validate': NumberValidator
|
||||
}
|
||||
]
|
||||
else:
|
||||
storage_questions = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'data_folder_path',
|
||||
'message': 'Path to folder, that will contains data (Relative or Absolute):',
|
||||
'default': str(default_config['storage'].get('data_folder_path', '')),
|
||||
'validate': NotNullValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'max_file_count',
|
||||
'message': 'Maximum count of file that will be saved:',
|
||||
'default': str(default_config['storage'].get('max_file_count', '')),
|
||||
'validate': NumberValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'max_read_records_count',
|
||||
'message': 'Count of messages to get from storage and send to ThingsBoard:',
|
||||
'default': str(default_config['storage'].get('max_read_records_count', '')),
|
||||
'validate': NumberValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'max_records_per_file',
|
||||
'message': 'Maximum count of records that will be stored in one file:',
|
||||
'default': str(default_config['storage'].get('max_records_per_file', '')),
|
||||
'validate': NumberValidator
|
||||
}
|
||||
]
|
||||
|
||||
storage_answers = prompt(storage_questions, style=style)
|
||||
|
||||
connectors_questions = [
|
||||
{
|
||||
'type': 'checkbox',
|
||||
'name': 'connectors',
|
||||
'message': 'Choose connectors you want to use:',
|
||||
'choices': [
|
||||
{
|
||||
'name': 'MQTT',
|
||||
},
|
||||
{
|
||||
'name': 'FTP',
|
||||
},
|
||||
{
|
||||
'name': 'Modbus',
|
||||
},
|
||||
{
|
||||
'name': 'CAN',
|
||||
},
|
||||
{
|
||||
'name': 'Bacnet',
|
||||
},
|
||||
{
|
||||
'name': 'BLE',
|
||||
},
|
||||
{
|
||||
'name': 'OPC-UA',
|
||||
},
|
||||
{
|
||||
'name': 'ODBC',
|
||||
},
|
||||
{
|
||||
'name': 'Request',
|
||||
},
|
||||
{
|
||||
'name': 'REST',
|
||||
},
|
||||
{
|
||||
'name': 'SNMP'
|
||||
}
|
||||
],
|
||||
'validate': lambda answer: 'You must choose at least one connector.' if len(answer) == 0 else True
|
||||
}
|
||||
]
|
||||
|
||||
connectors_answers = prompt(connectors_questions, style=style)
|
||||
|
||||
connectors_list = []
|
||||
|
||||
for connector in connectors_answers['connectors']:
|
||||
print(colored(f'Configuration {connector} connector:', 'blue'))
|
||||
connector_questions = [
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'name',
|
||||
'message': 'Name of connector:',
|
||||
'validate': NotNullValidator
|
||||
},
|
||||
{
|
||||
'type': 'input',
|
||||
'name': 'config',
|
||||
'message': 'Config file of connector:',
|
||||
'validate': NotNullValidator
|
||||
}
|
||||
]
|
||||
connector_answers = prompt(connector_questions, style=style)
|
||||
connectors_list.append({'type': connector.lower(), **connector_answers})
|
||||
|
||||
generate_config_file(
|
||||
{
|
||||
'thingsboard': {**base_answers, 'security': security_answers,
|
||||
'qos': qos_and_storage_type_answers['qos']},
|
||||
'storage': {'type': qos_and_storage_type_answers['storage'], **storage_answers},
|
||||
'connectors': connectors_list
|
||||
})
|
||||
except Exception:
|
||||
print(colored('Something went wrong! Please try again.', color='red'))
|
||||
raise SystemExit
|
||||
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user