Added option to add comments to the addresses lists (inline and file mode)

This commit is contained in:
BlueFox 2025-05-17 22:35:34 +02:00
parent e06cc1d4fc
commit 44b48485be
2 changed files with 55 additions and 48 deletions

View File

@ -76,7 +76,14 @@ def tab1_load_file():
window.tab1_label_help.show() window.tab1_label_help.show()
# clear listWidget and add the items # clear listWidget and add the items
window.tab1_listWidget.clear() window.tab1_listWidget.clear()
window.tab1_listWidget.addItems(tasmotonov.TasmotonovRunner("file", filename, action, False, False).get_addresses()) addresses = tasmotonov.TasmotonovRunner("file", filename, action, False, False).get_addresses()
items_to_add = []
for address, comment in addresses.items():
if comment == "":
items_to_add.append(address)
else:
items_to_add.append(f"{comment} [{address}]")
window.tab1_listWidget.addItems(items_to_add)
def tab1_clear(): def tab1_clear():
global filename global filename
filename = "" filename = ""
@ -107,7 +114,14 @@ def tab2_plainTextEdit_change():
content = window.tab2_plainTextEdit.toPlainText() content = window.tab2_plainTextEdit.toPlainText()
if content != "": if content != "":
tasmotonov_runner = tasmotonov.TasmotonovRunner("inline", content, action, False, False) tasmotonov_runner = tasmotonov.TasmotonovRunner("inline", content, action, False, False)
window.tab2_listWidget.addItems(tasmotonov_runner.get_addresses()) addresses = tasmotonov_runner.get_addresses()
items_to_add = []
for address, comment in addresses.items():
if comment == "":
items_to_add.append(address)
else:
items_to_add.append(f"{comment} [{address}]")
window.tab2_listWidget.addItems(items_to_add)
window.tab3_textBrowser.append(str(tasmotonov_runner.logger.log_string)) window.tab3_textBrowser.append(str(tasmotonov_runner.logger.log_string))
def tab2_action(): def tab2_action():
content = window.tab2_plainTextEdit.toPlainText() content = window.tab2_plainTextEdit.toPlainText()

View File

@ -99,82 +99,75 @@ class TasmotonovRunner:
raise ValueError(error_string) raise ValueError(error_string)
self.logger.log(f'Initialized runner: source={source}, data={data}, action={action}, verbose={verbose}') self.logger.log(f'Initialized runner: source={source}, data={data}, action={action}, verbose={verbose}')
self.tasmota_addresses = self.remove_invalid_addresses(self.clean_addresses(self.parse_addresses())) # returns a list self.set_valid_addresses_and_comments(self.parse_addresses())
self.logger.log(f'Validated addresses: distilled the following data. Devices added: {self.tasmota_addresses}')
def parse_addresses(self): def parse_addresses(self):
# convert the given adresses to a list # convert the given adresses to a list
tasmota_addresses = [] tasmota_addresses_raw = []
if self.source == 'file': if self.source == 'file':
try: try:
self.logger.log('Now trying to open the given file...', end='') self.logger.log('Now trying to open the given file...', end='')
with open(self.data, 'r') as file: with open(self.data, 'r') as file:
contents = file.read() contents = file.read()
if ';' in contents: if ',' in contents:
tasmota_addresses = contents.split(';') tasmota_addresses_raw = contents.split(',')
elif ',' in contents: elif ';' in contents:
tasmota_addresses = contents.split(',') tasmota_addresses_raw = contents.split(';')
elif '\n' in contents: elif '\n' in contents:
tasmota_addresses = contents.split('\n') tasmota_addresses_raw = contents.split('\n')
elif ' ' in contents:
tasmota_addresses = contents.split(' ')
else: else:
tasmota_addresses = [contents] tasmota_addresses_raw = [contents]
self.logger.log('Done.') self.logger.log('Done.')
self.logger.log(f'Collected addresses: {tasmota_addresses}') self.logger.log(f'Collected addresses: {tasmota_addresses_raw}')
except FileNotFoundError as e: except FileNotFoundError as e:
self.logger.log_error(f'Failed reading addresses. File with the path "{self.data} can\'t be opened because it doesn\'t exist. Exiting.') self.logger.log_error(f'Failed reading addresses. File with the path "{self.data} can\'t be opened because it doesn\'t exist. Exiting.')
raise e raise e
elif self.source == 'inline': elif self.source == 'inline':
self.logger.log('Now reading from inline... ', end='') self.logger.log('Now reading from inline... ', end='')
if ';' in self.data: if ',' in self.data:
tasmota_addresses = self.data.split(';') tasmota_addresses_raw = self.data.split(',')
elif ',' in self.data: elif ';' in self.data:
tasmota_addresses = self.data.split(',') tasmota_addresses_raw = self.data.split(';')
elif '\n' in self.data: elif '\n' in self.data:
tasmota_addresses = self.data.split('\n') tasmota_addresses_raw = self.data.split('\n')
elif ' ' in self.data:
tasmota_addresses = self.data.split(' ')
else: else:
tasmota_addresses = [self.data] tasmota_addresses_raw = [self.data]
self.logger.log('Done.') self.logger.log('Done.')
self.logger.log(f'Collected addresses: {tasmota_addresses}') self.logger.log(f'Collected addresses: {tasmota_addresses_raw}')
return tasmota_addresses_raw
return tasmota_addresses def set_valid_addresses_and_comments(self, tasmota_addresses_raw):
def clean_addresses(self, addresses_raw):
# clean them up (e.g. remove newlines if the file has a random newline somewhere
tasmota_addresses_cleaned = []
for address in addresses_raw:
to_add = address.replace('\n', '').replace(';', '').replace(',', '').replace(' ', '')
if to_add != '':
tasmota_addresses_cleaned.append(to_add)
return tasmota_addresses_cleaned
def remove_invalid_addresses(self, addressse_raw):
# now check data for consistency and integrity (is it really an ip address or a domain name?) # now check data for consistency and integrity (is it really an ip address or a domain name?)
# remove the ones that are not valid and log a warning # remove the ones that are not valid and log a warning
tasmota_addresses_validated = [] tasmota_addresses_validated = {}
self.logger.log('Now validating given addresses...') self.logger.log('Now validating given addresses...')
for address in addressse_raw: for address in tasmota_addresses_raw:
if is_ip(address) or is_fqdn(address): address_split = address.split("#") # split to separate existing comments
tasmota_addresses_validated.append(address) address_cleaned = address_split[0].replace('\n', '').replace(';', '').replace(',', '').replace(' ', '')
if len(address_split) > 1:
comment = address.split("#")[-1].lstrip()
else: else:
self.logger.log_warning(f'The address "{address}" is neither a valid IP address nor a FQDN / domain name; SKIPPING THIS ONE.') comment = ""
self.logger.log('Validated given adddresses.') if address_cleaned != "":
if is_ip(address_cleaned) or is_fqdn(address_cleaned):
tasmota_addresses_validated[address_cleaned] = comment
else:
self.logger.log_warning(f'The address "{address_cleaned}" (comment: "{comment}") is neither a valid IP address nor a FQDN / domain name; SKIPPING THIS ONE.')
return tasmota_addresses_validated self.tasmota_addresses = tasmota_addresses_validated
# some getters # some getters
def get_addresses(self): def get_addresses(self):
return self.tasmota_addresses return self.tasmota_addresses
def get_address(self, index): def get_address(self, index):
return self.tasmota_addresses[index] return {list(self.tasmota_addresses.keys())[index]: list(self.tasmota_addresses.values())[index]}
def run_custom_action(self, action): def run_custom_action(self, action):
# and finally turn on or off or toggle the lights! # and finally turn on or off or toggle the lights!
for address in self.tasmota_addresses: for address in list(self.tasmota_addresses.keys()):
self.logger.log('Running the action "{action}" on the following device: {address}') self.logger.log(f'Running the action "{action}" on the following device: {address}')
try: try:
answer = requests.get(f'http://{address}/cm?cmnd=Power%20{str(action).lower()}') answer = requests.get(f'http://{address}/cm?cmnd=Power%20{str(action).lower()}')
success = True success = True
@ -190,8 +183,8 @@ class TasmotonovRunner:
self.run_custom_action(self.action) self.run_custom_action(self.action)
def run_single_custom_action(self, index, action): def run_single_custom_action(self, index, action):
address = self.tasmota_addresses[index] address = list(self.tasmota_addresses.keys())[index]
self.logger.log('Running the action "{action}" on the following device: {address}') self.logger.log(f'Running the action "{action}" on the following device: {address}')
try: try:
answer = None answer = None
answer = requests.get(f'http://{address}/cm?cmnd=Power%20{str(action).lower()}') answer = requests.get(f'http://{address}/cm?cmnd=Power%20{str(action).lower()}')
@ -217,10 +210,10 @@ if __name__ == '__main__':
prog='Tasmotonov - simply toggle multiple tasmota lights', prog='Tasmotonov - simply toggle multiple tasmota lights',
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
description='A very simple script which allows you to turn on/off multiple tasmota devices specified.', description='A very simple script which allows you to turn on/off multiple tasmota devices specified.',
epilog='Info: if you choose a file as source, this files needs to contain the addresses of the tasmota devices either comma-separated, semicolon-separated, space-separated, or newline-separated!\n\n© Benjamin Burkhardt, 2025') epilog='Info: if you choose a file as source, this files needs to contain the addresses of the tasmota devices either comma-separated, semicolon-separated, or newline-separated (each entry can have a comment starting with a # (hashtag)!\n\n© Benjamin Burkhardt, 2025')
parser.add_argument('source', help='Select either to read the adresses (of the devices) from a "file" or from "inline"', choices=['file', 'inline']) parser.add_argument('source', help='Select either to read the adresses (of the devices) from a "file" or from "inline"', choices=['file', 'inline'])
parser.add_argument('data', help='Either the path to the file, or a comma-, space- or semicolon-separated list of tasmota adresses.') parser.add_argument('data', help='Either the path to the file, or a comma-, semicolon- or newline-separated list of tasmota adresses.')
parser.add_argument('action', help='Select to turn all tasmota devices "on" or "off" or "toggle" (case insensitive)', choices=['on', 'off', 'toggle']) parser.add_argument('action', help='Select to turn all tasmota devices "on" or "off" or "toggle" (case insensitive)', choices=['on', 'off', 'toggle'])
parser.add_argument('-v', '--verbose', help='Turn on verbose file output', action='store_true') parser.add_argument('-v', '--verbose', help='Turn on verbose file output', action='store_true')
parser.add_argument('--version', action='version', version=f'Tasmotonov.py {version}') parser.add_argument('--version', action='version', version=f'Tasmotonov.py {version}')