diff --git a/build_otaconf_gateway.sh b/build_otaconf_gateway.sh index 8bbc886b30c9d158ba7aa0badaa955151ac24f04..f18a14d4d0c88ddb5d3d48c3e52afb9cf3632682 100755 --- a/build_otaconf_gateway.sh +++ b/build_otaconf_gateway.sh @@ -3,8 +3,11 @@ name="../otaconf-gateway.tapp" app="src/otaconf/gateway-node/autoexec.be" -module1="src/otaconf/otaconf.be" -# module2="src/mesh.be" +module1="src/otaconf/gateway-node/globals.be" +module2="src/otaconf/gateway-node/main.be" +module3="src/otaconf/gateway-node/init/init_commands.be" +module4="src/otaconf/gateway-node/init/init_mesh.be" +module5="src/otaconf/gateway-node/actions/action_register_cluster.be" build="build" temp="build/temp" @@ -13,7 +16,10 @@ mkdir "$build" mkdir "$temp" cp "$app" "$temp" cp "$module1" "$temp" -# cp "$module2" "$temp" +cp "$module2" "$temp" +cp "$module3" "$temp" +cp "$module4" "$temp" +cp "$module5" "$temp" rm -rf "$name" cd "$temp" diff --git a/src/otaconf/gateway-node/actions/action_register_cluster.be b/src/otaconf/gateway-node/actions/action_register_cluster.be new file mode 100644 index 0000000000000000000000000000000000000000..63029ba702471d15178cbb589e220a91fb0d41fc --- /dev/null +++ b/src/otaconf/gateway-node/actions/action_register_cluster.be @@ -0,0 +1,3 @@ +def _action_register_cluster(closure) + print('yey registered') +end \ No newline at end of file diff --git a/src/otaconf/gateway-node/autoexec.be b/src/otaconf/gateway-node/autoexec.be index 5961a152db78453825e328cb7d17d0d2a5abc6d2..8df2e10009bfa052d0e70dff1d9e27f17d7a49c7 100644 --- a/src/otaconf/gateway-node/autoexec.be +++ b/src/otaconf/gateway-node/autoexec.be @@ -1,244 +1,13 @@ -import mqtt -import json -import otaconf -import persist -import mesh +# load global variables +load(tasmota.wd + 'globals.be') -var otaconf_finished = false +# load init +load(tasmota.wd + 'init_commands.be') +load(tasmota.wd + 'init_mesh.be') +# load(tasmota.wd + 'init_nbiot.be') -var _otaconf_timer_id = 'otaconf_timer' -var _wifi_used = tasmota.cmd('Wifi')['Wifi'] == 'ON' -var _mesh_scan_retries = 3 -var _configuration_uuid = nil -var _configuration_files = nil -var _configuration_commands = nil -var _own_command = nil +# load actions +load(tasmota.wd + 'action_register_cluster.be') - -def _set_ota_conf_interval(cmd, idx, payload) - if otaconf.set_poll_interval(payload) - tasmota.resp_cmnd_done() - else - tasmota.resp_cmnd_failed() - end -end - -def _set_ota_server_url(cmd, idx, payload) - otaconf.set_server_url(payload) - tasmota.resp_cmnd_done() -end - -def _otaconf_timeout() - otaconf_finished = true - - tasmota.log('OTA: configuration timed out', 1) -end - -def _otaconf_failed() - otaconf_finished = true - - tasmota.log('OTA: configuration failed', 1) -end - -def _finish_otaconf(early_exit) - if !early_exit - persist.last_otaconf_poll = tasmota.rtc()['local'] - persist.save() - end - - otaconf_finished = true - - if _own_command != nil - tasmota.cmd(_own_command) - end - - tasmota.log('OTA: configuration finished', 2) -end - -def _config_status_updated() - tasmota.remove_timer(_otaconf_timer_id) - - _finish_otaconf(false) -end - -def _update_config_status() - if _configuration_uuid != nil - otaconf.update_config_status(_configuration_uuid, _config_status_updated) - tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) - else - _otaconf_failed() - end -end - -def _execute_commands() - while _configuration_commands.size() > 0 - var configuration_command = _configuration_commands[0] - var device_macs = configuration_command['macs'] - var command = configuration_command['command'] - - for device_mac : device_macs - if device_mac == otaconf.get_mac() - _own_command = command - else - tasmota.log('OTA: remotely executing command on device: ' + device_mac, 2) - - var payload = {'action': 'EXECUTE_COMMAND', 'command': command} - mesh.send(otaconf.format_mac(device_mac), json.dump(payload)) - end - end - - _configuration_commands.remove(0) - end - - _update_config_status() -end - -def _distribute_files() - tasmota.log('OTA: distributing files', 2) - - _execute_commands() -end - -def _devices_registered() - tasmota.remove_timer(_otaconf_timer_id) - - _distribute_files() -end - -def _check_devices_registered() - var devices = mesh.get_peers() - var unregistered_devices = otaconf.get_unregistered_devices(devices) - - if unregistered_devices.size() > 0 - otaconf.register_devices(unregistered_devices, _devices_registered) - tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) - else - _distribute_files() - end -end - -def _scan_mesh() - if _mesh_scan_retries > 0 - otaconf.send_mesh_scan_request() - _mesh_scan_retries -= 1 - tasmota.set_timer(1000, _scan_mesh) - else - _check_devices_registered() - end -end - -def _process_config(response) - tasmota.remove_timer(_otaconf_timer_id) - - if response != nil && response.contains('configuration') - _configuration_uuid = response['uuid'] - _configuration_files = response['configuration']['files'] - _configuration_commands = response['configuration']['commands'] - - if _configuration_files.size() > 0 - tasmota.log('OTA: attempting to fetch files', 2) - - for file : _configuration_files - tasmota.log('OTA: fetching file: ' + file['uuid'], 2) - - if file.contains('url') - tasmota.urlfetch(file['url'], file['uuid']) - else - tasmota.urlfetch(otaconf.get_server_url() + '/clusters/' + otaconf.get_cluster_uuid() + '/files/' + file['uuid']) - end - end - end - - if response['configuration'].contains('mesh_scan') && response['configuration']['mesh_scan'] - _scan_mesh() - else - _distribute_files() - end - else - _finish_otaconf(false) - end -end - -def _check_config_pending() - otaconf.request_pending_config(_process_config) - tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) -end - -def _cluster_registered(response) - tasmota.remove_timer(_otaconf_timer_id) - - if response == nil - _otaconf_failed() - end - - _check_config_pending() -end - -def _check_cluster_registered() - if !otaconf.is_cluster_registered() - otaconf.register_custer(_cluster_registered) - tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) - else - _check_config_pending() - end -end - -def _check_otaconf() - var next_otaconf_time = int(persist.find('last_otaconf_poll')) + otaconf.get_poll_interval() - var current_time = tasmota.rtc()['local'] - - if !persist.has('last_otaconf_poll') || (current_time > next_otaconf_time) - _check_cluster_registered() - else - var remaining_hours = str((next_otaconf_time - current_time) / 3600) - var remaining_min = str(((next_otaconf_time - current_time) % 3600) / 60) - tasmota.log('OTA: next configuration poll in ~ ' + remaining_hours + ' hours ' + remaining_min + ' minutes', 2) - _finish_otaconf(true) - end -end - -def _init_nbiot() - print('init nb-iot') # todo implement -end - -def _callback(sender, payload) - var recieved_data = json.load(payload) - print(recieved_data) # todo remove - print(mesh.get_peers()) # todo remove - - if recieved_data != nil && recieved_data.contains('action') - var action = recieved_data['action'] - end -end - -def _mesh_init() - mesh.start() - mesh.register_callback(_callback) -end - -def _mesh_pre_init() - mesh.init_soft_ap_mode(1) - - tasmota.set_timer(50, _mesh_init) -end - -def _wakeup_task() - - if !_wifi_used - _init_nbiot() - _mesh_pre_init() - else - _mesh_init() - end - - tasmota.set_timer(1000, _check_otaconf) -end - -if !_wifi_used - tasmota.add_rule('system#init', _wakeup_task) -else - tasmota.add_rule('system#boot', _wakeup_task) -end - -tasmota.add_cmd('OtaConfInterval', _set_ota_conf_interval) -tasmota.add_cmd('OtaServerUrl', _set_ota_server_url) +# load main +load(tasmota.wd + 'main.be') \ No newline at end of file diff --git a/src/otaconf/gateway-node/autoexec_old.be b/src/otaconf/gateway-node/autoexec_old.be new file mode 100644 index 0000000000000000000000000000000000000000..fea1c10a1d65e9f15e12e39b9a5a282876e2b012 --- /dev/null +++ b/src/otaconf/gateway-node/autoexec_old.be @@ -0,0 +1,254 @@ +import mqtt +import json +import otaconf +import persist +import mesh + +var otaconf_finished = false + +var _otaconf_timer_id = 'otaconf_timer' +var _wifi_used = tasmota.cmd('Wifi')['Wifi'] == 'ON' +var _mesh_scan_retries = 3 +var _file_send_retries = 3 +var _file_in_process = nil +var _configuration_uuid = nil +var _configuration_files = nil +var _configuration_commands = nil +var _own_command = nil + +def _otaconf_timeout() + otaconf_finished = true + + tasmota.log('OTA: configuration timed out', 1) +end + +def _otaconf_failed() + otaconf_finished = true + + tasmota.log('OTA: configuration failed', 1) +end + +def _finish_otaconf(early_exit) + if !early_exit + persist.last_otaconf_poll = tasmota.rtc()['local'] + persist.save() + end + + otaconf_finished = true + + if _own_command != nil + tasmota.cmd(_own_command) + end + + tasmota.log('OTA: configuration finished', 2) +end + +def _config_status_updated() + tasmota.remove_timer(_otaconf_timer_id) + + _finish_otaconf(false) +end + +def _update_config_status() + if _configuration_uuid != nil + otaconf.update_config_status(_configuration_uuid, _config_status_updated) + tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) + else + _otaconf_failed() + end +end + +def _execute_commands() + while _configuration_files.size() > 0 + var configuration_file = _configuration_files[0] + var device_macs = configuration_command['macs'] + var command = configuration_command['command'] + + # todo move to otaconf + for device_mac : device_macs + if device_mac == otaconf.get_mac() + _own_command = command + else + tasmota.log('OTA: remotely executing command on device: ' + device_mac, 2) + + var payload = {'action': 'EXECUTE_COMMAND', 'command': command} + mesh.send(otaconf.format_mac(device_mac), json.dump(payload)) + end + end + + _configuration_commands.remove(0) + end + + _update_config_status() +end + +def _send_batch + +def _distribute_files() + while _configuration_commands.size() > 0 + var configuration_command = _configuration_commands[0] + var device_macs = configuration_command['macs'] + var command = configuration_command['command'] + + # todo move to otaconf + for device_mac : device_macs + if device_mac == otaconf.get_mac() + _own_command = command + else + tasmota.log('OTA: remotely executing command on device: ' + device_mac, 2) + + var payload = {'action': 'EXECUTE_COMMAND', 'command': command} + mesh.send(otaconf.format_mac(device_mac), json.dump(payload)) + end + end + + _configuration_commands.remove(0) + end + + _execute_commands() +end + +def _devices_registered() + tasmota.remove_timer(_otaconf_timer_id) + + _distribute_files() +end + +def _check_devices_registered() + var devices = mesh.get_peers() + var unregistered_devices = otaconf.get_unregistered_devices(devices) + + if unregistered_devices.size() > 0 + otaconf.register_devices(unregistered_devices, _devices_registered) + tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) + else + _distribute_files() + end +end + +def _scan_mesh() + if _mesh_scan_retries > 0 + otaconf.send_mesh_scan_request() + _mesh_scan_retries -= 1 + tasmota.set_timer(5000, _scan_mesh) + else + _check_devices_registered() + end +end + +def _process_config(response) + tasmota.remove_timer(_otaconf_timer_id) + + if response != nil && response.contains('configuration') + _configuration_uuid = response['uuid'] + _configuration_files = response['configuration']['files'] + _configuration_commands = response['configuration']['commands'] + + # todo move to otaconf + if _configuration_files.size() > 0 + tasmota.log('OTA: attempting to fetch files', 2) + + for file : _configuration_files + tasmota.log('OTA: fetching file: ' + file['uuid'], 2) + + if file.contains('url') + tasmota.urlfetch(file['url'], file['uuid']) + else + tasmota.urlfetch(otaconf.get_server_url() + '/clusters/' + otaconf.get_cluster_uuid() + '/files/' + file['uuid']) + end + end + end + + if response['configuration'].contains('mesh_scan') && response['configuration']['mesh_scan'] + _scan_mesh() + else + _distribute_files() + end + else + _finish_otaconf(false) + end +end + +def _check_config_pending() + otaconf.request_pending_config(_process_config) + tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) +end + +def _cluster_registered(response) + tasmota.remove_timer(_otaconf_timer_id) + + if response == nil + _otaconf_failed() + end + + _check_config_pending() +end + +def _check_cluster_registered() + if !otaconf.is_cluster_registered() + otaconf.register_custer(_cluster_registered) + tasmota.set_timer(3000, _otaconf_timeout, _otaconf_timer_id) + else + _check_config_pending() + end +end + +def _check_otaconf() + var next_otaconf_time = int(persist.find('last_otaconf_poll')) + otaconf.get_poll_interval() + var current_time = tasmota.rtc()['local'] + + if !persist.has('last_otaconf_poll') || (current_time > next_otaconf_time) + _check_cluster_registered() + else + var remaining_hours = str((next_otaconf_time - current_time) / 3600) + var remaining_min = str(((next_otaconf_time - current_time) % 3600) / 60) + tasmota.log('OTA: next configuration poll in ~ ' + remaining_hours + ' hours ' + remaining_min + ' minutes', 2) + _finish_otaconf(true) + end +end + +def _init_nbiot() + print('init nb-iot') # todo implement +end + +def _callback(sender, payload) + var recieved_data = json.load(payload) + print(recieved_data) # todo remove + print(mesh.get_peers()) # todo remove + + if recieved_data != nil && recieved_data.contains('action') + var action = recieved_data['action'] + end +end + +def _mesh_init() + mesh.start() + mesh.register_callback(_callback) +end + +def _mesh_pre_init() + mesh.init_soft_ap_mode(1) + + tasmota.set_timer(50, _mesh_init) +end + +def _wakeup_task() + + if !_wifi_used + _init_nbiot() + _mesh_pre_init() + else + _mesh_init() + end + + tasmota.set_timer(1000, _check_otaconf) +end + +if !_wifi_used + tasmota.add_rule('system#init', _wakeup_task) +else + tasmota.add_rule('system#boot', _wakeup_task) +end + +tasmota.add_cmd('OtaConfInterval', _set_ota_conf_interval) +tasmota.add_cmd('OtaServerUrl', _set_ota_server_url) diff --git a/src/otaconf/gateway-node/globals.be b/src/otaconf/gateway-node/globals.be new file mode 100644 index 0000000000000000000000000000000000000000..a3faadc4d3cdf60573794fc1eea0eb4eb2f0e59c --- /dev/null +++ b/src/otaconf/gateway-node/globals.be @@ -0,0 +1,13 @@ +import persist + +# global variables exposed to the application developer +var otaconf_finished = false + +# global variables for internal use +var _g_wifi_used = tasmota.cmd('Wifi')['Wifi'] == 'ON' +var _g_mesh_init = false +var _g_nbiot_init = false +var _g_request_topic = 'otaconf/%s/request' +var _g_response_topic = 'otaconf/%s/response' +var _g_server_url = persist.find('otaconf_server_url', '') +var _g_poll_interval = int(persist.find('otaconf_poll_interval', 86400)) # default: 24h \ No newline at end of file diff --git a/src/otaconf/gateway-node/init/init_commands.be b/src/otaconf/gateway-node/init/init_commands.be new file mode 100644 index 0000000000000000000000000000000000000000..e1a563fca6f0cf461bd28538e0471c83934a2951 --- /dev/null +++ b/src/otaconf/gateway-node/init/init_commands.be @@ -0,0 +1,24 @@ +import re + +def _set_ota_conf_interval(cmd, idx, payload) + if re.match('^[0-9]*$', payload) != nil + _g_poll_interval = payload + + persist.otaconf_poll_interval = payload + persist.save() + + tasmota.resp_cmnd_done() + else + tasmota.resp_cmnd_failed() + end +end + +def _set_ota_server_url(cmd, idx, payload) + _g_server_url = payload + persist.otaconf_server_url = payload + persist.save() + tasmota.resp_cmnd_done() +end + +tasmota.add_cmd('OtaConfInterval', _set_ota_conf_interval) +tasmota.add_cmd('OtaServerUrl', _set_ota_server_url) \ No newline at end of file diff --git a/src/otaconf/gateway-node/init/init_mesh.be b/src/otaconf/gateway-node/init/init_mesh.be new file mode 100644 index 0000000000000000000000000000000000000000..6e4182e0d62a998d7ca86f81e480c48e04e140b0 --- /dev/null +++ b/src/otaconf/gateway-node/init/init_mesh.be @@ -0,0 +1,25 @@ +import mesh + +def _mesh_start() + mesh.start() + + _g_mesh_init = true +end + +# init soft accesspoint mode +def _init_soft_ap_mode() + mesh.init_soft_ap_mode(1) + + tasmota.set_timer(50, _mesh_start) +end + +# init standard accesspoint stationary mode +def _init_ap_sta_mode() + _mesh_start() +end + +if !_g_wifi_used + tasmota.add_rule('system#init', _init_soft_ap_mode) +else + tasmota.add_rule('system#boot', _init_ap_sta_mode) +end \ No newline at end of file diff --git a/src/otaconf/gateway-node/main.be b/src/otaconf/gateway-node/main.be new file mode 100644 index 0000000000000000000000000000000000000000..cb66182e2d2e36330fae070ff0aff846734f641d --- /dev/null +++ b/src/otaconf/gateway-node/main.be @@ -0,0 +1,47 @@ +import persist + +def _finish_otaconf(early_exit) + if !early_exit + persist.otaconf_last_poll = tasmota.rtc()['local'] + persist.save() + end + + otaconf_finished = true + + #if _own_command != nil + # tasmota.cmd(_own_command) + #end + + tasmota.log('OTA: configuration finished', 2) +end + +def _check_otaconf() + var next_otaconf_time = int(persist.find('otaconf_last_poll')) + _g_poll_interval + var current_time = tasmota.rtc()['local'] + + if !persist.has('otaconf_last_poll') || (current_time > next_otaconf_time) + _action_register_cluster() + else + var remaining_hours = str((next_otaconf_time - current_time) / 3600) + var remaining_min = str(((next_otaconf_time - current_time) % 3600) / 60) + + tasmota.log('OTA: next configuration poll in ~ ' + remaining_hours + ' hours ' + remaining_min + ' minutes', 2) + + _finish_otaconf(true) + end +end + +# todo first check if otaconf necessary, then check mesh etc +def _wake_up_task() + if !_g_mesh_init # || (!_g_wifi_used && !_nbiot_init) + tasmota.set_timer(1000, _wake_up_task) + end + + tasmota.set_timer(1000, _check_otaconf) +end + +if !_g_wifi_used + tasmota.add_rule('system#init', _wake_up_task) +else + tasmota.add_rule('system#boot', _wake_up_task) +end \ No newline at end of file diff --git a/src/otaconf/gateway-node/modules/nbiot b/src/otaconf/gateway-node/modules/nbiot new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/otaconf/otaconf.be b/src/otaconf/gateway-node/otaconf.be similarity index 95% rename from src/otaconf/otaconf.be rename to src/otaconf/gateway-node/otaconf.be index 0d52f14211b0ced87c095fa4f86c19aae31757c8..16146513200cc3e0f0c470c67f9408e0d6be1fec 100644 --- a/src/otaconf/otaconf.be +++ b/src/otaconf/gateway-node/otaconf.be @@ -4,6 +4,12 @@ import mqtt import mesh import re +class FileSender + var _ + + def +end + class ResponseHandler def handle_cluster_registered(response) if !response.contains('content') || !response['content'].contains('uuid') @@ -129,18 +135,6 @@ otaconf.init = def (m) return self.poll_interval end - def set_poll_interval(interval) - if re.match('^[0-9]*$', interval) != nil - self.poll_interval = interval - persist.otaconf_poll_interval = interval - persist.save() - - return true - else - return false - end - end - def get_server_url() return self.server_url end diff --git a/src/otaconf/sensor-node/autoexec.be b/src/otaconf/sensor-node/autoexec.be index d613f6e030763add51b1b25ea9f026783a7df3ed..5999aa63c2240d4f9a2c8a3026991b590056922f 100644 --- a/src/otaconf/sensor-node/autoexec.be +++ b/src/otaconf/sensor-node/autoexec.be @@ -97,14 +97,14 @@ end def _wakeup_task() - if !_wifi_used + if !_g_wifi_used _mesh_pre_init() else _mesh_init() end end -if !_wifi_used +if !_g_wifi_used tasmota.add_rule('system#init', _wakeup_task) else tasmota.add_rule('system#boot', _wakeup_task)