#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
# @Author: carlosgilgonzalez
# @Date:   2019-03-12T18:52:56+00:00
# @Last modified by:   carlosgilgonzalez
# @Last modified time: 2019-11-17T05:04:02+00:00

# upydev CLI
import argparse
import subprocess
import time
import sys
import json
import os
import shlex
import ast
import requests
import nmap
import netifaces
import re
import upydev
import socket
import struct
import logging
import glob
import string
import getpass
import secrets
import signal
import hashlib
import locale
import textwrap
from binascii import hexlify
from pathlib import Path
import multiprocessing
from datetime import datetime, date, timedelta
from sys import platform as _platform
import argcomplete
from argcomplete.completers import ChoicesCompleter
from upydevice import W_UPYDEVICE, BASE_WS_DEVICE, BASE_SERIAL_DEVICE
from upydevice.phantom import UOS
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa, ec
from cryptography.hazmat.primitives import serialization
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes

AUTHMODE_DICT = {0: 'NONE', 1: 'WEP', 2: 'WPA PSK', 3: 'WPA2 PSK',
                    4: 'WPA/WAP2 PSK'}
ADC_PINS_DICT = {'esp32h': [i for i in range(32, 40)]}

ATTEN_DICT = {0: 'ADC.ATTN_0DB', 1: 'ADC.ATTN_2_5DB', 2: 'ADC.ATTN_6DB',
              3: 'ADC.ATTN_11DB'}
ATTEN_INFO = """
0 = ADC.ATTN_0DB: 0dB attenuation, gives a maximum input voltage of 1.00v
1 = ADC.ATTN_2_5DB: 2.5dB attenuation, gives a maximum input voltage of approximately 1.34v
2 = ADC.ATTN_6DB: 6dB attenuation, gives a maximum input voltage of approximately 2.00v
3 = ADC.ATTN_11DB: 11dB attenuation, gives a maximum input voltage of approximately 3.6v
"""
log_levs = ['debug', 'info', 'warning', 'error', 'critical']

bloc_progress = ["▏", "▎", "▍", "▌", "▋", "▊", "▉"]


def serial_ports():
    ls_cmd_str = "/dev/tty.*"
    # print('Available Serial ports are:')
    # for port in glob.glob(ls_cmd_str):
    #     print(port)
    return glob.glob(ls_cmd_str)


def see_groups():
    avoid_files = ['upydev_.config', 'help.config', 'esp32h.config']
    local_cwd = [group_file.split('.')[0] for group_file in os.listdir(
    ) if '.config' in group_file and group_file not in 'upydev_.config']
    globl_wd = [group_file.split('.')[0] for group_file in os.listdir(
        upydev.__path__[0]) if '.config' in group_file and group_file not in avoid_files]
    return local_cwd + globl_wd


def see_global_devs():
    # avoid_files = ['upydev_.config', 'help.config', 'esp32h.config']
    # # local_cwd = [group_file.split('.')[0] for group_file in os.listdir(
    # # ) if '.config' in group_file and group_file not in 'upydev_.config']
    # globl_wd = [group_file.split('.')[0] for group_file in os.listdir(
    #     upydev.__path__[0]) if '.config' in group_file and group_file not in avoid_files]
    try:
        with open('{}/UPY_G.config'.format(upydev.__path__[0]), 'r', encoding='utf-8') as group:
            devices = json.loads(group.read())
            # print(devices)
        devs = list(devices.keys())
        return devs
    except Exception as e:
        return []


#############################################
# ARGPARSER

helparg = '''Mode:
- config : to save upy device settings (see -p, -t, -g),
            so the target and password arguments wont be required any more
- put : to upload a file to upy device (see -f, -s, -fre, -dir, -rst)
- get : to download a file from upy device (see -f, -s, -fre, -dir)
- sync : for a faster transfer of large files
    (this needs sync_tool.py in upy device) (see -f, -s and -lh)
- d_sync: to recursively sync a folder in upydevice filesystem use -dir
            to indicate the folder (must be in cwd), use -tree to see dir
            structure, to sync to an Sd card mounted as 'sd' use -s sd
- cmd : to send command to upy device ; (see -c, -r, -rl);
        example: upydev cmd -c "led.on()" ; upydev cmd -r "print('Hello uPy')";
                upydev cmd -rl "function_that_print_multiple_lines()";
    * tip: simple commands can be used without quotes;
        but for commands with parenthesis or special characters use quotes,
        for example: 'dummy_func()' ; use double quotes "" when the command
        includes a string like this example: "uos.listdir('/sd')"
- wrepl : to enter the terminal WebREPL; CTRL-x to exit, CTRL-d to do soft reset
        To see more keybindings info do CTRL-k
        (Added custom keybindings and autocompletion on tab to the previous work
        see: https://github.com/Hermann-SW/webrepl for the original work)
- wssrepl : to enter the terminal WebSecureREPL; CTRL-x to exit, CTRL-d to do soft reset
        To see more keybindings info do CTRL-k. REPL over WebSecureSockets (This needs use of
        'sslgen_key -tfkey', 'update_upyutils' and enable WebSecureREPL in the device "import wss_repl;wss_repl.start(ssl=True)")
- srepl : to enter the terminal serial repl using picocom, indicate port by -port option
        (to exit do CTRL-a, CTRL-x)
- ping : pings the target to see if it is reachable, CTRL-C to stop \n
- run : just calls import 'script', where 'script' is indicated by -f option
        (script must be in upydevice or in sd card indicated by -s option
        and the sd card must be already mounted as 'sd');
        * Supports CTRL-C to stop the execution and exits nicely.
- install : install libs to '/lib' path with upip; indicate lib with -f option
- mpyx : to froze a module/script indicated with -f option, and save some RAM,
         it uses mpy-cross tool (see https://gitlab.com/alelec/mpy_cross)
- timeit: to measure execution time of a module/script indicated with -f option.
          This is an implementation of
          https://github.com/peterhinch/micropython-samples/tree/master/timed_function
- fw: to list or get available firmware versions, use -md option to indicate operation:
        to list do: "upydev fw -md list -b [BOARD]" board can be e.g. 'esp32','esp8266' or 'PYBD'
                    "upydev fw -md list latest -b [BOARD]" to see the latest firmware available
        to get do: "upydev fw -md get [firmware file]" or "upydev fw -md get latest -b[BOARD]"
        * for list or get modes the -n option will filter the results further: e.g. -n ota
        to see available serial ports do: "upydev fw -md list serial_ports"
- flash: to flash a firmware file to the upydevice, a serial port must be indicated
            to flash do: "upydev flash -port [serial port] -f [firmware file]"
- see: to get specific command help info indicated with -c option. To get specific
        info about a devices group use -G option as "see -G [GROUP NAME]"
- find: to get a list of possible upy devices. Scans the local network to find devices
        with port 8266 (WebREPL) open. Use -n option to perform n scans (A single scan
        may not find all the devices)
- diagnose: to make a diagnostic test of the device (sends useful to commands
            to get device state info), to save report to file see -rep, use -n to save
            the report with a custom name (automatic name is "upyd_ID_DATETIME.txt")
            Use "-md local" option if connected to esp AP.
- errlog: if 'error.log' is present in the upydevice, this shows the content
            (cat('error.log')), if 'error.log' in sd use -s sd
- stream_test: to test download speed (from device to host). Default test is 10 MB of
               random bytes are sent in chunks of 20 kB and received in chunks of 32 kB.
               To change test parameters use -chunk_tx , -chunk_rx, and -total_size.
- sysctl : to start/stop a script without following the output. To follow initiate
           wrepl/srepl as normal, and exit with CTRL-x (webrepl) or CTRL-A,X (srepl)
           TO START: use -start [SCRIPT_NAME], TO STOP: use -stop [SCRIPT_NAME]
- log: to log the output of a upydevice script, indicate script with -f option, and
        the sys.stdout log level and file log level with -dslev and -dflev (defaults
        are debug for sys.stdout and error for file). To log in background use -daemon
        option, then the log will be redirected to a file with level -dslev.
        To stop the 'daemon' log mode use -stopd and indicate script with -f option.
        'Normal' file log and 'Daemon' file log are under .upydev_logs folder in $HOME
        directory, named after the name of the script. To follow an on going 'daemon'
        mode log, use -follow option and indicate the script with -f option.
- update_upyutils: to update the latest versions of sync_tool.py, upylog.py,
                upynotify.py, upysecrets.py, upysh2.py, ssl_repl.py, uping.py, time_it.py,
                wss_repl.py and wss_helper.py.
- debug: to execute a local script line by line in the target upydevice, use -f option
        to indicate the file. To enter next line press ENTER, to finish PRESS C
        then ENTER. To break a while loop do CTRL+C.

- gen_rsakey: To generate RSA-2048 bit key that will be shared with the device
              (it is unique for each device) use -tfkey to send this key to the
              device (use only if connected directly to the AP of the device or a
              "secure" wifi e.g. local/home). If not connected to a "secure" wifi
              upload the key (it is stored in upydev.__path__) by USB/Serial connection.
- rf_wrkey: To "refresh" the WebREPL password with a new random password derivated from
            the RSA key previously generated. A token then is sent to the device to generate
            the same password from the RSA key previously uploaded. This won't leave
            any clues in the TCP Websocekts packages of the current WebREPL password.
            (Only the token will be visible; check this using wireshark)
            (This needs upysecrets.py)

- crypto_wrepl: To enter the terminal CryptoWebREPL a E2EE wrepl/shell terminal;
             CTRL-x to exit, CTRL-u to toggle encryption mode (enabled by default)
             To see more keybindings info do CTRL-k. By default resets after exit,
             use -rkey option to refresh the WebREPL password with a new random password,
             after exit.This passowrd will be stored in the working directory or in global directory with
             -g option. (This mode needs upysecrets.py)
- upy: to access crypto_wrepl in a 'ssh' style command to be used like e.g.:
      "upydev upy@192.168.1.42" or if a device is stored in a global group called "UPY_G" (this
       needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 192.168.1.42 myfoopass")
       The device can be accessed as "upydev upy@foo_device" or redirect any command as e.g.
       "upydev ping -@foo_device"

- sslgen_key: (This needs openssl available in $PATH)
               To generate ECDSA key and a self-signed certificate to enable SSL sockets
               This needs a passphrase, that will be required every time the key is loaded.
               Use -tfkey to upload this key to the device
               (use only if connected directly to the AP of the device or a
               "secure" wifi e.g. local/home). If not connected to a "secure" wifi
               upload the key (it is stored in upydev.__path__) by USB/Serial connection.

- ssl_wrepl: To enter the terminal SSLWebREPL a E2EE wrepl/shell terminal (SSL sockets);
             CTRL-x to exit, CTRL-u to toggle encryption mode (enabled by default)
             To see more keybindings info do CTRL-k. By default resets after exit,
             use -rkey option to refresh the WebREPL password with a new random password,
             after exit.This passowrd will be stored in the working directory or in global directory with
             -g option. (This mode needs ssl_repl.py, upysecrets.py for -rfkey)
             *(Use -nem option to use without encryption (for esp8266))
- ssl: to access ssl_wrepl in a 'ssh' style command to be used like e.g.:
      "upydev ssl@192.168.1.42" or if a device is stored in a global group called "UPY_G" (this
       needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 192.168.1.42 myfoopass")
       The device can be accessed as "upydev ssl@foo_device" or redirect any command as e.g.
       "upydev ping -@foo_device". *(For esp8266 use the option -nem (no encryption mode))

- sh_srepl: To enter the serial terminal SHELL-REPL; CTRL-x to exit,
            To see more keybindings info do CTRL-k. By default resets after exit.
            To configure a serial device use -t for baudrate and -p for serial port
            To access without previous configuration: "sh_srepl -port [serial port] -b [baudrate]"
            (default baudrate is 115200)
            To access with previous configuration:
                - "sh_srepl" (if device configured in current working directory)
                - "sh_srepl -@ foo_device" (if foo_device is configured in global group 'UPY_G')

- shr: to access the serial terminal SHELL-REPL in a 'ssh' style command to be used like e.g.:
      "upydev shr@/dev/tty.usbmodem3370377430372" or if a device is stored in a global group called "UPY_G" (this
       needs to be created first doing e.g.
       "upydev make_group -g -f UPY_G -devs foo_device 115200 /dev/tty.usbmodem3370377430372")
       The device can be accessed as "upydev shr@foo_device"

- wssl: to access ssl_wrepl if WebSecureREPL is enabled in a 'ssh' style command to be used like e.g.:
      "upydev wssl@192.168.1.42" or if a device is stored in a global group called "UPY_G" (this
       needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 192.168.1.42 myfoopass")
       then the device can be accessed as "upydev wssl@foo_device"
- set_wss: To toggle between WebSecureREPL and WebREPL, to enable WebSecureREPL do 'set_wss', to disable 'set_wss -wss'
- jupyterc: to run MicroPython upydevice kernel for jupyter console, CTRL-D to exit, %%lsmagic to see magic commands and
            how to connect to a device either WebREPL (%%websocketconnect) or Serial connection (%%serialconnect).
            Hit tab to autcomplete magic commands, and MicroPython/Python code.
            (This needs jupyter and MicroPython upydevice kernel to be installed)

- make_group: to make a group of boards to send commands to. Use -f for the name
              of the group and -devs option to indicate a name, ip and the
              password of each board. (To store the group settings globally use -g option)

- mg_group: to manage a group of boards to send commands to. Use -G for the name
              of the group and -add option to add devices (indicate a name, ip and the
              password of each board) or -rm to remove devices (indicated by name)
* GROUP COMMAND MODE:
        To send a command to multiple devices in a group (made with make_group
        command) use -G option as -G [GROUP NAME]; to target specific devices
        within a group add -devs option as -devs [DEV NAME] [DEV NAME] ...
        (upydev will use local working directory configuration unless it does
        not find any or manually indicated with -g option)
        * This sends the command to one device at a time;
        ** For parallel command execution use -GP option (instead of -G)

upy Commands:
> GENERAL
- info : for upy device system info
- id : for upy device unique id
- upysh : to enable the upy shell in the upy device (then do 'upydev man' to
        access upysh manual info)
- reset : to do a soft reset in upy device
- kbi : sends CTRL-C signal to stop an ongoing loop, to be able to access repl again
- uhelp : just calls micropython help
- umodules: just calls micropython help('modules')
- mem_info : for upy device RAM memory info; call it once to check actual memory,
            call it twice and it will free some memory
- filesize : to get the size of file in root dir (default) or sd with '-s sd' option;
            if no file name indicated with -f option, prints all files
- filesys_info : to get memory info of the file system, (total capacity, free, used),
            (default root dir, -s option to change)
- netinfo : for upy device network info if station is enabled and connected to an AP
- netinfot : same as netinfo but in table format
- netscan : for upy device network scan
- netstat_on : to enable STA
- netstat_off : to disable STA
- netstat_conn : to connect to an AP , must provide essid and password (see -wp)
- netstat : STA state ; returns True if enabled, False if disabled
- ap_on : to enable AP
- ap_off : to disable AP
- apstat : AP state ; returns True if enabled, False if disabled
- apconfig : AP configuration of essid and password with authmode WPA/WPA2/PSK,
            (see -ap), needs first that the AP is enabled (do 'upydev ap_on')
- apscan : scan devices connected to AP; returns number of devices and mac address
- i2c_config : to configure the i2c pins (see -i2c, defaults are SCL=22, SDA=23)
- i2c_scan : to scan i2c devices (must config i2c pins first)
- spi_config: to configure the spi pins (see -spi, defaults are SCK=5,MISO=19,MOSI=18,CS=21)
- set_localtime: to pass host localtime and set upy device rtc
- set_ntptime: to set rtc from server, (see -utc for time zone)
- get_datetime: to get date and time (must be set first, see above commands)

> WIFI UTILS (needs wifiutils.py in upydevice, see upyutils in upydev github repo)
- wlan_init: Initiates wlan util (call this before the following commands)
- wsta_config: Saves a "netowrk STA" configuration json file in upydevice, use with -wp option as -wp 'ssid' 'password'
- wap_config: Saves a "netowrk AP" configuration json file in upydevice, use with -ap option as -ap 'ssid' 'password'
- wsta_conn: Connects to the wlan configured with the command wsta_config
- wap_conn: Enables the upydevice AP configured with the command wap_config

> SD
- sd_enable: to enable/disable the LDO 3.3V regulator that powers the SD module
             use -po option to indicate the Pin.
- sd_init: to initialize the sd card; (spi must be configured first)
           create sd object and mounts as a filesystem, needs sdcard.py see
           https://github.com/Carglglz/upydev/blob/master/DOCS/Documentation.md#sd_init
- sd_deinit: to unmount sd card
- sd_auto: experimental command, needs a sd module with sd detection pin
           and the SD_AM.py script. Enable an Interrupt
           with the sd detection pin, so it mounts the sd when is detected,
           and unmount the sd card when is extracted. See more info in:
           https://github.com/Carglglz/upydev/blob/master/DOCS/Documentation.md#sd_auto
>INPUT
Sensors:
    * ADC:
        ^ON BOARD ADCS:
        - adc_config : to config analog pin to read from (see pinout, -po and -att)
        - aread : to read from an analog pin previously configured

        ^EXTERNAL ADC: (I2C) ADS1115 ***
        - ads_init : to initialize and configure ADS1115 and the channel to
                    read from (see -ads, -ch)
        - ads_read : to read from analog pin previously configured
                (see -tm option for stream mode, and -f for logging*)
                * for one shot read, logging is also available with -f and
                    -n option (for tagging)
                * use '-f now' for automatic 'log_mode_datetime.txt' name.
                * for stream mode profiling use -tm [ms] -ads test
    * IMU:
        - imu_init : initialize IMU, use -imu option to indicate the imu library.
                    (default option is 'lsm9ds1', see sensor requirements for more info')
        - imuacc : one shot read of the IMU lineal accelerometer (g=-9.8m/s^2),
                (see -tm option for stream mode, and -f for logging*)
                * for one shot read, logging is also available with -f and
                    -n option (for tagging)
                * use '-f now' for automatic 'log_mode_datetime.txt' name.
                * for stream mode profiling use -tm [ms] -imu test
                ** stream mode and logging are supported in imugy and imumag also
        - imuacc_sd: log the imuacc data to the sd (must be mounted)
                    with the file format 'log_mode_datetime.txt'
                    (see -tm option for stream mode)
        - imugy :  one shot read of the IMU gyroscope (deg/s)
        - imumag : one shot read of the IMU magnetometer (gauss)
    * WEATHER SENSOR: (BME280)
        - bme_init: initialize bme, use -bme option to indicate the weather sensor library.
                    (default option is 'bme280', see sensor requirements for more info')
        - bme_read : to read values from bme (Temp(C), Pressure(Pa), Rel.Hummidity (percentage))
                (see -tm option for stream mode, and -f for logging*)
                * for one shot read, logging is also available with -f and
                    -n option (for tagging)
                * use '-f now' for automatic 'log_mode_datetime.txt' name.
                * for stream mode profiling use -tm [ms] -bme test
    * POWER SENSOR: (INA219)
        - ina_init: initialize ina, use -ina option to indicate the power sensor library.
                    (default option is 'ina219', see sensor requirements for more info')
        - ina_read : to read values from ina (Pot.Diff (Volts), Current(mA), Power(mW))
                (see -tm option for stream mode, and -f for logging*)
                * for one shot read, logging is also available with -f and
                    -n option (for tagging)
                * use '-f now' for automatic 'log_mode_datetime.txt' name.
                * for stream mode profiling use -tm [ms] -ina test
        - ina_batt: Use the sensor to profile battery usage and estimate battery life left.
                    It will made 100 measurements during 5 seconds. Indicate battery capacity
                    with -batt option; (in mAh)

>OUTPUT:
    * DAC:
        - dac_config: to config analog pin to write to (use -po option)
        - dac_write: to write a value in volts (0-3.3V)
        - dac_sig: to write a signal use -sig for different options:
                  > [type] [Amp] [frequency]
                    (type: 'sin, sq'; Amp 0-1 V ; fq:0-50 (above that fq loses quality))
                  > start : starts signal generation
                  > stop : stops signal
                  > mod [Amp] [frequency]: modify the signal with the Amp and fq indicated.
    * BUZZER:
        - buzz_config: to config PWM pin to drive the buzzer (use -po option)
        - buzz_set_alarm: to set an alarm at time indicated with option -at, be
                         aware that the rtc time must be set first with set_localtime
                         or set_ntptime
        - buzz_interrupt: to configure an interrupt with pins indicated with -po,
                        use -md 'rev' for interrupt reverse operation
        - buzz_beep: make the buzzer beep, with options set by -opt,
                    usage: buzz_beep -opt [beep_ms] [number_of_beeps] [time_between_beeps] [fq]
    * DC MOTOR:
        - dcmotor_config: to config PWM pins to drive a DC motor (use -po option as -po [DIR1] [DIR2])
        - dcmotor_move: to move the motor to one direction ['R'] or the opposite ['L']
                        use -to option as -to ['R' or 'L'] [VELOCITY] (60-512)
        - dcmotor_stop: to stop the DC motor
    * SERVO:
        - servo_config: to configure the servo pin with -po option
        - servo_angle: to move the servo an angle indicated by -opt option
    * STEPPER MOTOR:
        - stepper_config: to configure the step and direction pin with -po option
                            *( -po [DIR_PIN] [STEP_PIN])
        - stepper_move: to move the stepper to right or left, at a velocity and
                       a numbers of steps indicated with -to option: [R or L] [velocity] [# steps]
                       R: right, L:left, velocity (1000-20000) (smaller is faster) and
                       # steps (int), where 200 steps means a complete lap

NETWORKING:
    * MQTT:
        - mqtt_config: to set id, broker address, user and password, use with -client option
                       as "mqtt_config -client [ID] [BROKER ADDRESS] [USER] [PASSWORD]"
        - mqtt_conn: to start a mqtt client and connect to broker; use mqtt_config first
        - mqtt_sub: to subscribe to a topic, use -to option as "mqtt_sub -to [TOPIC]"
        - mqtt_pub: to publish to a topic, use -to option as "mqtt_pub -to [TOPIC] [PAYLOAD]" or
                    "mqtt_pub -to [PAYLOAD]" if already subscribed to a topic.
        - mqtt_check: to check for new messages of the subscribed topics.
    * SOCKETS:
        - socli_init: to initiate a socket client use with -server option as
                      "socli_init -server [IP] [PORT] [BUFFER LENGTH]"
        - socli_conn: to connect the socket client to a server (inidcated by IP)
        - socli_send: to send a message to the server, use -n option to indicate
                      the message
        - socli_recv: to receive a message from the server
        - socli_close: to close the client socket
        - sosrv_init: to initiate a socket server, use with -server option as
                      "sosrv_init -server [PORT] [BUFFER LENGTH]"
        - sosrv_start: to start the server, waits for a connection
        - sosrv_send: to send a message to the client, use -n option to indicate
                      the message
        - sosrv_recv: to receive a message from the client
        - sosrv_close: to close the server socket
    * UREQUEST:
        - rget_json: to make a request to API that returns a JSON response format
                    (indicate API URL with -f option)
        - rget_text: to make a request to API that returns a text response format
                    (indicate API URL with -f option)

Port/board specific commands:
- battery : if running on battery, gets battery voltage (esp32 huzzah feather)
- pinout : to see the pinout reference/info of a board, indicated by -b option,
           to request a single or a list of pins info use -po option
- specs : to see the board specs, indicated by -b option
- pin_status: to see pin state, to request a specific set use -po option ***

* ESP32: (Not implemented yet)
    - touch
    - hall
    - deepsleep
    - temp
'''

usag = """%(prog)s [Mode] [options] or %(prog)s [upy command] [options] \n
This means that if the first argument is not a Mode keyword or a
upy command keyword it assumes it is a 'raw' upy command to send to the upy device \n
Needs webrepl enabled in upy device;
see http://docs.micropython.org/en/latest/esp32/quickref.html#webrepl-web-browser-interactive-prompt"""

# UPY MODE KEYWORDS AND COMMANDS
keywords_mode = ['put', 'get', 'sync', 'cmd', 'config', 'info', 'netinfo',
                 'netscan', 'i2c_scan', 'id', 'reset', 'upysh', 'wrepl',
                 'battery', 'mem_info', 'ping', 'filesize', 'filesys_info',
                 'uhelp', 'umodules', 'netinfot', 'netstat_on', 'netstat_off',
                 'netstat', 'ap_on', 'ap_off', 'apstat', 'apscan', 'run',
                 'apconfig', 'netstat_conn', 'i2c_config', 'imu_init',
                 'imuacc', 'imugy', 'imumag', 'pinout', 'specs', 'install',
                 'pin_status', 'adc_config', 'aread', 'set_ntptime',
                 'get_datetime', 'set_localtime', 'imuacc_sd', 'ads_init',
                 'ads_read', 'sd_enable', 'spi_config', 'sd_init', 'sd_deinit',
                 'sd_auto', 'dac_config', 'dac_write', 'dac_sig',
                 'buzz_config', 'buzz_set_alarm', 'buzz_interrupt',
                 'buzz_beep', 'servo_config', 'servo_angle', 'stepper_config',
                 'stepper_move', 'dcmotor_config', 'dcmotor_move',
                 'dcmotor_stop', 'mpyx', 'timeit', 'mqtt_config', 'mqtt_conn',
                 'mqtt_pub', 'mqtt_sub', 'mqtt_check', 'socli_init',
                 'socli_conn', 'socli_send', 'socli_recv', 'socli_close',
                 'sosrv_init', 'sosrv_start', 'sosrv_send', 'sosrv_recv',
                 'sosrv_close', 'rget_json', 'rget_text', 'fw', 'flash',
                 'see', 'bme_init', 'bme_read', 'ina_init', 'ina_read',
                 'ina_batt', 'make_group', 'srepl', 'mg_group', 'find',
                 'wlan_init', 'wsta_config', 'wap_config', 'wsta_conn',
                 'wap_conn', 'kbi', 'diagnose', 'errlog', 'd_sync',
                 'stream_test', 'sysctl', 'log', 'update_upyutils',
                 'debug', 'gen_rsakey', 'rf_wrkey', 'crypto_wrepl', 'upy',
                 'sslgen_key', 'ssl_wrepl', 'ssl', 'sh_srepl', 'shr',
                 'wssl', 'wssrepl', 'set_wss', 'jupyterc']

help_dv = "To point the command to a specific device saved in the global group"
# ARG PARSER
parser = argparse.ArgumentParser(prog='upydev',
                                 description='Command line tool for wireless Micropython devices',
                                 formatter_class=argparse.RawTextHelpFormatter,
                                 usage=usag, prefix_chars='-')
parser.version = 'upydev: 0.3.1'
parser.add_argument(
    "m", metavar='Mode', help=helparg).completer = ChoicesCompleter(keywords_mode)
parser.add_argument("-@", help=help_dv, required=False).completer = ChoicesCompleter(see_global_devs())
parser.add_argument("-p", help='password', required=False)
parser.add_argument("-f", help='script or file name', required=False)
parser.add_argument(
    "-lh", help='local ip for sync mode if can not be automatically detected',
    required=False)
parser.add_argument(
    "-t", help='target host for example : 192.168.1.40 or for AP 192.168.4.1',
    required=False)
parser.add_argument(
    "-sec", help='to configure a device password with no stream trace',
    required=False, default=False, action='store_true')
parser.add_argument(
    "-s", help='source dir in upy device, default is root dir (flash memory); sd for sd card source dir mounted as "/sd"', required=False)
parser.add_argument(
    "-dir", help='target dir in upy device, default is root dir (flash memory)', required=False)
parser.add_argument(
    "-tree", help='to see the tree structure of a the directory to sync', required=False, default=False, action='store_true')
parser.add_argument(
    "-c", help='command to send to upy device, do not wait for a response',
    required=False, type=str).completer = ChoicesCompleter(keywords_mode)
parser.add_argument(
    "-r", help='command to send to upy device, waits for a short response (one line)', required=False)
parser.add_argument(
    "-rl", help='command to send to upy device, waits for a longer response (multiple lines), so it can catch tracebacks messages', required=False)
parser.add_argument(
    "-g", help='to store/read the configuration file globally, if there is no config file in working directory, \n it uses the global file',
    required=False, default=False, action='store_true')
parser.add_argument(
    "-st", help='shows target ip if using config file', required=False, default=False, action='store_true')
parser.add_argument(
    "-rst", help='reset flag after put file operation, default true, "f" to disable', required=False)
parser.add_argument(
    "-ap", help='[essid] [password] to set AP name and password',
    required=False, nargs=2)
parser.add_argument(
    "-wp", help='[essid] [password] to connect the STA to an AP',
    required=False, nargs=2)
parser.add_argument(
    "-i2c", help='[SCL] [SDA] to config scl and sda i2c pins in upy device',
    required=False, nargs=2, default=[22, 23], type=int)
parser.add_argument(
    "-spi", help='[SCk] [MISO] [MOSI] [CS] to config scl and sda i2c pins in upy device',
    required=False, nargs=4, default=[5, 19, 18, 21], type=int)
parser.add_argument(
    "-b", help='[BOARD NAME] to request info of specs or of the pinouts',
    required=False).completer = ChoicesCompleter(['esp32', 'esp8266'])
parser.add_argument(
    "-att",
    help='[attenuation] for attenuate adc input, default is 11dB attenuation, do -att info for more info',
    required=False, default=3)
parser.add_argument(
    "-tm",
    help='[timeout] enable stream mode and indicates a timeout in milliseconds',
    required=False, type=int)
parser.add_argument(
    "-po",
    help='[pin] indicates the pin to request info',
    required=False, nargs='+', type=int)

parser.add_argument(
    "-opt",
    help='wildcard option to be used by several commands',
    required=False, nargs='+', type=int)

parser.add_argument(
    "-n",
    help='tag a log shot from a sensor or adc',
    required=False)
parser.add_argument("-wss",
                    help='Use WebSocket Secure (not available for all commands), this needs WebSecureREPL enabled "wss_repl.start(ssl=True)"',
                    default=False, action='store_true')
parser.add_argument('-v', action='version')
parser.add_argument('-start', help='To start a script with sysctl; use as -start [SCRIPT_NAME]',
                    required=False)
parser.add_argument('-stop', help='To start a script with sysctl; use as -stop [SCRIPT_NAME]',
                    required=False)
parser.add_argument('-dflev', help='debug file mode level, options [debug, info, warning, error, critical]; default=error', default='error').completer = ChoicesCompleter(log_levs)
parser.add_argument('-dslev', help='debug sys.stdout mode level, options [debug, info, warning, error, critical]; default=debug', default='debug').completer = ChoicesCompleter(log_levs)
parser.add_argument('-daemon', help='enable "daemon mode", uses nohup so this means running in background, output if any is redirected to [SCRIPT_NAME]_daemon.log', default=False, action='store_true')
parser.add_argument('-stopd', help='To stop a log daemon script', default=False, action='store_true')
parser.add_argument('-follow', help='To follow a daemon log script file, indicate script with -f option', default=False, action='store_true')
parser.add_argument('-rep', help='to save the report in a text file',
                    required=False, default=False, action='store_true')
parser.add_argument('-apmd', help='set target to 192.168.4.1',
                    required=False, default=False, action='store_true')
parser.add_argument('-chunk_tx', help='chunk size of data packets in kB to send for wifi speed test',
                    required=False, default=20, type=int)
parser.add_argument('-chunk_rx', help='chunk size of data packets in kB to receive for wifi speed test',
                    required=False, default=32, type=int)
parser.add_argument('-total_size', help='total size of data packets in MB for wifi speed test',
                    required=False, default=10, type=int)
parser.add_argument('-show_key', help='show RSA key',
                    required=False, default=False, action='store_true')
parser.add_argument('-tfkey', help='transfer RSA/ECDSA key,ideally this should be done only if connected to the AP of the device',
                    required=False, default=False, action='store_true')
parser.add_argument('-key_size', help='Indicate RSA key length in bits (default is 2048)',
                    required=False, default=2048, type=int)
parser.add_argument('-rkey', help='To refresh password after WebREPL disconnection',
                    required=False, default=False, action='store_true')
parser.add_argument('-localkey_id', help='To refresh password manually of a current WebREPL connection',
                    required=False)
parser.add_argument('-nem', help='To initiate CryptoWebREPL in unencrypted mode only',
                    required=False, default=False, action='store_true')
parser.add_argument('-utc', help='utc zone for set_nptime command', type=int,
                    default=0)
parser.add_argument('-imu', help='to select the imu library',
                    default='lsm9ds1')
parser.add_argument('-ads', help='to select the ads library and config options',
                    default='ads1115')
parser.add_argument('-ch', help='to select the ads analog channel to read from',
                    type=int, default=0)
parser.add_argument('-bme', help='to select the bme library and config options',
                    default='bme280')
parser.add_argument('-ina', help='to select the ina library and config options',
                    default='ina219')
parser.add_argument('-batt', help='to indicate battery capacity',
                    type=int, nargs='+')
parser.add_argument('-sig', help='to indicate a value or type of signal to write',
                    nargs='+')
parser.add_argument('-at', help='[HOUR] [MINUTE] [SECONDS]',
                    nargs='+', type=int, default=[0, 0, 0])
parser.add_argument('-to', help='[DIRECTION] [VELOCITY] [STEPS]',
                    nargs='+', default=['R', 2000, 100])

parser.add_argument('-client', help='[ID] [BROKER ADDRESS] [USER] [PASSWORD]',
                    nargs='+', default=[None, 'test.mosquitto.org'])
parser.add_argument('-server', help='For Client Socket:[IP] [PORT] [BUFFER LENGTH]; Server Socket [PORT] [BUFFER LENGTH]',
                    nargs='+')
parser.add_argument('-md', help='for command suboptions',
                    nargs='+').completer = ChoicesCompleter(['list', 'get', 'serial_ports', 'latest'])
parser.add_argument(
    '-port', help='serial port of the device to flash to').completer = ChoicesCompleter(serial_ports())
parser.add_argument('-devs', help='to indicate the devices that will be part of a group, use as -devs [DEV_1] [IP_1] [PASS_1] [DEV_2]...',
                    nargs='+')
parser.add_argument('-add', help='to indicate the devices that will be added to a group, use as -add [DEV_1] [IP_1] [PASS_1] [DEV_2]...',
                    nargs='+')
parser.add_argument('-rm', help='to indicate the devices that will be removed from a group, use as -rm [DEV_1] [DEV_2]...',
                    nargs='+')
parser.add_argument(
    '-G', help='to indicate the group of devices that the command is directed to').completer = ChoicesCompleter(see_groups())
parser.add_argument('-fre', help='special option to put or get files from upy device, can be "cwd", an expresion to match or name of files',
                    nargs='+')
parser.add_argument(
    '-GP', help='to indicate the group of devices that the command is directed to, for parallel command execution').completer = ChoicesCompleter(see_groups())
argcomplete.autocomplete(parser)
args = parser.parse_args()

#############################################
# TERMINAL SIZE
columns, rows = os.get_terminal_size(0)
cnt_size = 75
if columns > cnt_size:
    bar_size = int((columns - cnt_size))
    pb = True
else:
    bar_size = 1
    pb = False


def sortSecond(val):
    return val[1]


def _dt_format(number):
        rtc_n = str(number)
        if len(rtc_n) == 1:
            rtc_n = "0{}".format(rtc_n)
            return rtc_n
        else:
            return rtc_n


def _ft_datetime(t_now):
    return([_dt_format(i) for i in t_now])


class DisplayablePath(object):
    display_filename_prefix_middle = '├──'
    display_filename_prefix_last = '└──'
    display_parent_prefix_middle = '    '
    display_parent_prefix_last = '│   '

    def __init__(self, path, parent_path, is_last):
        self.path = Path(str(path))
        self.parent = parent_path
        self.is_last = is_last
        if self.parent:
            self.depth = self.parent.depth + 1
        else:
            self.depth = 0

    @property
    def displayname(self):
        if self.path.is_dir():
            return self.path.name + '/'
        return self.path.name

    @classmethod
    def make_tree(cls, root, parent=None, is_last=False, criteria=None):
        root = Path(str(root))
        criteria = criteria or cls._default_criteria

        displayable_root = cls(root, parent, is_last)
        yield displayable_root

        children = sorted(list(path
                               for path in root.iterdir()
                               if criteria(path)),
                          key=lambda s: str(s).lower())
        count = 1
        for path in children:
            is_last = count == len(children)
            if path.is_dir():
                yield from cls.make_tree(path,
                                         parent=displayable_root,
                                         is_last=is_last,
                                         criteria=criteria)
            else:
                yield cls(path, displayable_root, is_last)
            count += 1

    @classmethod
    def _default_criteria(cls, path):
        return True

    def displayable(self):
        if self.parent is None:
            return self.displayname

        _filename_prefix = (self.display_filename_prefix_last
                            if self.is_last
                            else self.display_filename_prefix_middle)

        parts = ['{!s} {!s}'.format(_filename_prefix,
                                    self.displayname)]

        parent = self.parent
        while parent and parent.parent is not None:
            parts.append(self.display_parent_prefix_middle
                         if parent.is_last
                         else self.display_parent_prefix_last)
            parent = parent.parent

        return ''.join(reversed(parts))


def get_fw_versions(keyword):
    fw_list = []
    fw_links = []
    r = requests.get('https://micropython.org/download/all/')
    fw_text = [line for line in r.text.split(
        '\n') if keyword in line and any(x in line for x in ['bin', 'dfu', 'zip']) and 'firmware' in line]
    for line in fw_text:
        for element in re.split(r"[=<>]+", line):
            if 'firmware' in element:
                fw_links.append('www.micropython.org{}'.format(element[1:-1]))
                fw_list.append(element[1:-1].split('/')[3])
    fw_dict = dict(zip(fw_list, fw_links))
    return fw_dict, fw_list


def serial_ports():
    ls_cmd_str = "/dev/tty.*"
    # print('Available Serial ports are:')
    # for port in glob.glob(ls_cmd_str):
    #     print(port)
    return glob.glob(ls_cmd_str)


def see_groups():
    avoid_files = ['upydev_.config', 'help.config', 'esp32h.config']
    local_cwd = [group_file.split('.')[0] for group_file in os.listdir(
    ) if '.config' in group_file and group_file not in 'upydev_.config']
    globl_wd = [group_file.split('.')[0] for group_file in os.listdir(
        upydev.__path__[0]) if '.config' in group_file and group_file not in avoid_files]
    return local_cwd + globl_wd


def see_global_devs():
    # avoid_files = ['upydev_.config', 'help.config', 'esp32h.config']
    # # local_cwd = [group_file.split('.')[0] for group_file in os.listdir(
    # # ) if '.config' in group_file and group_file not in 'upydev_.config']
    # globl_wd = [group_file.split('.')[0] for group_file in os.listdir(
    #     upydev.__path__[0]) if '.config' in group_file and group_file not in avoid_files]
    try:
        with open('{}/UPY_G.config'.format(upydev.__path__[0]), 'r', encoding='utf-8') as group:
            devices = json.loads(group.read())
            # print(devices)
        devs = list(devices.keys())
        return devs
    except Exception as e:
        return []


def run_command_rl(command):
    end = False
    lines = []
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    while end is not True:
        if process.poll() is None:
            output = process.stdout.readline()
            if output == '' and process.poll() is not None:
                break
            if output:
                line = output.strip().decode()
                lines.append(line)
                if output.strip() == b'### closed ###':
                    end = True
        else:
            break
    rc = process.poll()
    return rc, lines


def old_run_raw_cmd(cmd_str):
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass


def run_command_rt(command):
    end = False
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    while end is not True:
        if process.poll() is None:
            output = process.stdout.readline()
            if output == '' and process.poll() is not None:
                break
            if output:
                line = output.strip().decode()
                print(line)
                if output.strip() == b'### closed ###':
                    end = True
        else:
            break


def run_live(run_cmd, targ, password):
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, targ, password)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except KeyboardInterrupt:
        try:
            print('...closing...')
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print(message[:-1].decode())
            for i in range(10):
                proc.stdout.readlines()
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
        except KeyboardInterrupt:
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)


def get_ip():
    # scanoutput = subprocess.check_output(["ipconfig", "getifaddr", "en0"])
    # ip = scanoutput.decode('utf-8').split('\n')[0]
    try:
        ip = [netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for
                    iface in netifaces.interfaces() if netifaces.AF_INET in
                    netifaces.ifaddresses(iface)][-1]
        return ip
    except Exception as e:
        try:
            ip_soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            ip_soc.connect(('8.8.8.8', 1))
            local_ip = ip_soc.getsockname()[0]
            ip_soc.close()
            return local_ip
        except Exception as e:
            return '0.0.0.0'


def get_ssid():
    if _platform == "linux" or _platform == "linux2":
       # linux
       return ''
    elif _platform == "darwin":
       # MAC OS X
        scanoutput = subprocess.check_output(["airport", "-I"])
        wifi_info = [data.strip()
                     for data in scanoutput.decode('utf-8').split('\n')]
        wifi_info_dic = {data.split(':')[0]: data.split(
            ':')[1].strip() for data in wifi_info[:-1]}
        return wifi_info_dic['SSID']


def read_file_sync_raw(file_tosync, soc):
    # final_file = b''
    buff = bytearray(2000)
    with open(file_tosync, 'rb') as log:
        while True:
            try:
                buff[:] = log.read(2000)  # 2 KB
                # print(len(chunk))
                if buff != b'':
                    # in python use 'i'
                    soc.sendall(buff)
                    # final_file += chunk
                else:
                    print('END OF FILE')
                    # soc.sendall
                    break
            except Exception as e:
                print(e)
                pass


def sync_to_dev(file_tosync, dev):
    source = ''
    if args.s == 'sd':
        source = args.s + '/'
    # START A LOCAL SERVER
    local_ip = get_ip()
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((local_ip, 8005))
    server_socket.listen(1)
    dev.cmd_nb("sync_to_filesys('{}', '{}', 8005)".format(source+file_tosync, local_ip))
    conn, addr = server_socket.accept()
    conn.settimeout(5)
    read_file_sync_raw(file_tosync, conn)
    conn.close()


# def do_pg_bar(index, wheel, nb_of_total, speed, time_e, loop_l):
#     l_bloc = bloc_progress[loop_l]
#     if index == 80:
#         l_bloc = "█"
#     sys.stdout.write("\033[K")
#     print('▏{:<81}▏{:>5}{:>5} % | DATA: {} | SPEED: {:>5} MB/s | TIME: {} s'.format("█" *index + l_bloc,
#                                                                     wheel[index % 4],
#                                                                     int((index/80)*100),
#                                                                     nb_of_total, speed, str(timedelta(seconds=time_e)).split('.')[0][3:]), end='\r')
#     sys.stdout.flush()

def do_pg_bar(index, wheel, nb_of_total, speed, time_e,
              loop_l, percentage, size_bar=bar_size):
    l_bloc = bloc_progress[loop_l]
    if index == bar_size:
        l_bloc = "█"
    sys.stdout.write("\033[K")
    print('▏{}▏{:>2}{:>5} % | DATA: {} | SPEED: {:>5} MB/s | TIME: {} s'.format("█" *index + l_bloc  + " "*((bar_size+1) - len("█" *index + l_bloc)),
                                                                    wheel[index % 4],
                                                                    int((percentage)*100),
                                                                    nb_of_total, speed, str(timedelta(seconds=time_e)).split('.')[0][2:]), end='\r')
    sys.stdout.flush()


def w_stream_reader(soc, total_size, chunk_rx):
    buff = bytearray(0)
    loop_index = 0
    wheel = ['|', '/', '-', "\\"]
    t_start = time.time()
    while True:
        t0 = time.time()
        try:
            chunk = soc.recv(chunk_rx)  # 32 KB
            if chunk != b'':
                buff += chunk
                loop_index_f = ((len(buff))/total_size)*bar_size
                loop_index = int(loop_index_f)
                loop_index_l = int(round(loop_index_f-loop_index, 1)*6)
                nb_of_total = "{:.2f}/{:.2f} MB".format(len(buff)/(1024**2), total_size/(1024**2))
                percentage = len(buff)/total_size
                t_elapsed = time.time() - t_start
                t_speed = "{:^2.2f}".format((len(buff)/(1024**2))/t_elapsed)
                if pb:
                    do_pg_bar(loop_index, wheel, nb_of_total, t_speed,
                              t_elapsed, loop_index_l, percentage)
                if len(buff) == total_size:
                    break
            else:
                pass
                #print('END OF FILE')
                # soc.close()
                # break
        except Exception as e:
            if e == KeyboardInterrupt:
                break
            else:
                print('END OF FILE')
                break
    return buff


def w_stream_writer(soc):
    buff = os.urandom(20000)
    try:
        soc.sendall(buff)  # 20 kB
    except Exception as e:
        if e == KeyboardInterrupt:
            print(e)


def stream_test(dev, mode='download'):
    if mode == 'download':
        # START A LOCAL SERVER
        chunk_size_kb = args.chunk_tx
        _kB = 1024
        _MB = 1024*_kB
        print('DOWNLOAD SPEED TEST:')
        print('CHUNK TX DATA SIZE: {:>40.2f} kB'.format(chunk_size_kb))
        print('CHUNK RX DATA SIZE: {:>40.2f} kB'.format(args.chunk_rx))
        print('TOTAL TX DATA SIZE: {:>40.2f} MB\n'.format(args.total_size))
        local_ip = get_ip()
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind((local_ip, 8005))
        server_socket.listen(1)
        dev.cmd_nb("w_stream_writer('{}', 8005, {}, {})".format(local_ip, args.chunk_tx*_kB, args.total_size*_MB ))
        conn, addr = server_socket.accept()
        soc_timeout = 1
        conn.settimeout(soc_timeout)
        t0 = time.time()
        data_chunk = w_stream_reader(conn, args.total_size*_MB, args.chunk_rx*_kB)
        dt = (time.time()-t0)-soc_timeout
        conn.close()
        return (data_chunk, dt)


def get_live_stream(run_cmd, targ, password, sensorlib, filename=None,
                    r_format='fff', nb=12, log=False, variables=None):
    if args.lh is None:
        local_ip = get_ip()
    if args.lh is not None:
        local_ip = args.lh

    # START A LOCAL SERVER
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((local_ip, 8005))
    server_socket.listen(1)
    connect_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        '{}.connect_SOC({})'.format(sensorlib, "'{}'".format(
                                                     local_ip)), args.t, args.p)
    cmd_resp = run_command_rl(connect_cmd_str)
    conn, addr = server_socket.accept()
    conn.settimeout(1)
    flushed = 0
    while flushed == 0:
        try:
            conn.recv(1024)
        except Exception as e:
            flushed = 1
    if args.tm > 500:
        conn.settimeout((args.tm/1000)*3)  # for long periodic shots
    run_cmd_str = 'upycmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, targ, password)
    run_live_cmd = shlex.split(run_cmd_str)
    comm = subprocess.call(run_live_cmd)
    print(('{:^15}'*len(variables)).format(*variables))
    try:
        if log:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      struct.unpack(r_format, data_acc)))
                    decode_print = dict(zip(variables,
                                            [format(val, '.4f') for val in struct.unpack(r_format, data_acc)]))
                    sys.stdout.write("\033[K")
                    print(('{:^15}'*len(decode_print.values())
                           ).format(*decode_print.values()), end='\r')
                    sys.stdout.flush()
                    with open(filename, 'a') as logfile:
                        logfile.write(json.dumps(decode))
                        logfile.write('\n')
                except Exception as e:
                    print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break
        else:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      [format(val, '.4f') for val in struct.unpack(r_format, data_acc)]))
                    sys.stdout.write("\033[K")
                    print(('{:^15}'*len(decode.values())
                           ).format(*decode.values()), end='\r')
                    sys.stdout.flush()
                except Exception as e:
                    # print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break

    except KeyboardInterrupt:
        try:
            print('\n')
            print('...closing...')
            conn.shutdown(socket.SHUT_RDWR)
            time.sleep(1)
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            print('Done!')
            conn.close()
        except KeyboardInterrupt:
            print('...wait for closing...')
            conn.shutdown(socket.SHUT_RDWR)
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            conn.close()
            print('Done!')


def test_stream(run_cmd, targ, password, sensorlib, filename=None,
                r_format='fff', nb=12, log=False, variables=None, BUFFERSIZE=1):
    if args.lh is None:
        local_ip = get_ip()
    if args.lh is not None:
        local_ip = args.lh

    # START A LOCAL SERVER
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((local_ip, 8005))
    server_socket.listen(1)
    connect_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        '{}.connect_SOC({})'.format(sensorlib, "'{}'".format(
                                                     local_ip)), args.t, args.p)
    cmd_resp = run_command_rl(connect_cmd_str)
    conn, addr = server_socket.accept()
    conn.settimeout(1)
    flushed = 0
    while flushed == 0:
        try:
            conn.recv(1024)
        except Exception as e:
            flushed = 1
    if args.tm > 500:
        conn.settimeout((args.tm/1000)*3)  # for long periodic shots
    run_cmd_str = 'upycmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, targ, password)
    run_live_cmd = shlex.split(run_cmd_str)
    subprocess.call(run_live_cmd)
    print(('{:^15}'*len(variables)).format(*variables))
    t0 = time.time()
    test_val = []
    try:
        if log:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      struct.unpack(r_format, data_acc)))
                    decode_print = dict(zip(variables,
                                            [format(val, '.4f') for val in struct.unpack(r_format, data_acc)]))
                    sys.stdout.write("\033[K")
                    print(('{:^15}'*len(decode_print.values())
                           ).format(*decode_print.values()), end='\r')
                    sys.stdout.flush()
                    with open(filename, 'a') as logfile:
                        logfile.write(json.dumps(decode))
                        logfile.write('\n')
                except Exception as e:
                    print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break
        else:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      struct.unpack(r_format, data_acc)))
                    decode_p = dict(zip(variables,
                                        [format(val, '.4f') for val in struct.unpack(r_format, data_acc)]))
                    sys.stdout.write("\033[K")
                    print(('{:^15}'*len(decode_p.values())
                           ).format(*decode_p.values()), end='\r')
                    sys.stdout.flush()
                    test_val.append(decode)
                    if t0 == 0:
                        t0 = time.time()

                    final_time = abs(time.time()-t0)
                except Exception as e:
                    # print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break

    except KeyboardInterrupt:
        try:
            print('\n')
            print('...closing...')
            conn.shutdown(socket.SHUT_RDWR)
            time.sleep(1)
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            print('Done!')
            print('TEST RESULTS ARE:')
            print('TEST DURATION : {} (s)'.format(final_time))
            # # FIND SAMPLING RATE
            # Method 1:
            N_DATA_PACKETS = len(test_val)  # Number of batches received
            print('DATA PACKETS : {} packets'.format(N_DATA_PACKETS))
            # (assuming al batches = buffer_size long)
            Fs = ((N_DATA_PACKETS+1)*BUFFERSIZE/final_time)
            print('SAMPLES PER PACKET : {}'.format(BUFFERSIZE))
            print('VARIABLES PER SAMPLE : {}; {}'.format(
                len(variables), variables))
            print('SIZE OF PACKETS: {} bytes'.format(nb))
            #  32 is batch/buffer size ,
            # so total samples is n_batches (len(vals)) x size_batch
            # print('Period: {} ms ; Fs:{} Hz'.format(
            # timestamp[:].mean()/1e3,1/((timestamp[:].mean())/1e6))
            conn.close()
            print('Period: {} ms ; Fs:{} Hz, Data send rate: {} packets/s of {} samples'.format(
                round((1/Fs)*1e3), round(Fs, -1),
                round(N_DATA_PACKETS/final_time), BUFFERSIZE))
            print(
                'DATA TRANSFER RATE: {} KB/s'.format(round(N_DATA_PACKETS/final_time)*nb/1024))
        except KeyboardInterrupt:
            conn.shutdown(socket.SHUT_RDWR)
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            conn.close()
            print('Done!')


def get_live_stream_chunk(run_cmd, targ, password, sensorlib, filename=None,
                          r_format='h'*20, nb=40, log=False, variables=None):
    if args.lh is None:
        local_ip = get_ip()
    if args.lh is not None:
        local_ip = args.lh

    # START A LOCAL SERVER
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((local_ip, 8005))
    server_socket.listen(1)
    connect_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        '{}.connect_SOC({})'.format(sensorlib, "'{}'".format(
                                                     local_ip)), args.t, args.p)
    cmd_resp = run_command_rl(connect_cmd_str)
    conn, addr = server_socket.accept()
    conn.settimeout(1)
    flushed = 0
    while flushed == 0:
        try:
            conn.recv(1024)
        except Exception as e:
            flushed = 1
    if args.tm > 500:
        conn.settimeout((args.tm/1000)*3)  # for long periodic shots
    run_cmd_str = 'upycmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, targ, password)
    run_live_cmd = shlex.split(run_cmd_str)
    subprocess.call(run_live_cmd)
    time.sleep(0.5)
    print(('{:^15}'*len(variables)).format(*variables))
    try:
        if log:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      [struct.unpack(r_format, data_acc)]))
                    # print(decode)
                    for chunk in decode.values():
                        value = sum([val for val in chunk])/len(chunk)
                        sys.stdout.write("\033[K")
                        decode_p = dict(zip(variables,
                                            [format(value, '.4f')]))
                        print(('{:^15}'*len(decode_p.values())
                               ).format(*decode_p.values()), end='\r')
                        sys.stdout.flush()
                    with open(filename, 'a') as logfile:
                        logfile.write(json.dumps(decode))
                        logfile.write('\n')
                except Exception as e:
                    # print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break
        else:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      [struct.unpack(r_format, data_acc)]))
                    # print(decode)
                    for chunk in decode.values():
                        value = sum([val for val in chunk])/len(chunk)
                        sys.stdout.write("\033[K")
                        decode_p = dict(zip(variables,
                                            [format(value, '.4f')]))
                        print(('{:^15}'*len(decode_p.values())
                               ).format(*decode_p.values()), end='\r')
                        sys.stdout.flush()
                        # print(dict(zip(variables, [value])))

                except Exception as e:
                    # print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        print(e)
                        break

    except KeyboardInterrupt:
        try:
            print('\n')
            print('...closing...')
            conn.shutdown(socket.SHUT_RDWR)
            time.sleep(1)
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            print('Done!')
            conn.close()
        except KeyboardInterrupt:
            conn.shutdown(socket.SHUT_RDWR)
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            conn.close()
            print('Done!')


def test_stream_chunk(run_cmd, targ, password, sensorlib, filename=None,
                      r_format='h'*20, nb=40, log=False, variables=None, BUFFERSIZE=20):
    if args.lh is None:
        local_ip = get_ip()
    if args.lh is not None:
        local_ip = args.lh

    # START A LOCAL SERVER
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((local_ip, 8005))
    server_socket.listen(1)
    connect_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        '{}.connect_SOC({})'.format(sensorlib, "'{}'".format(
                                                     local_ip)), args.t, args.p)
    cmd_resp = run_command_rl(connect_cmd_str)
    conn, addr = server_socket.accept()
    conn.settimeout(1)
    flushed = 0
    while flushed == 0:
        try:
            conn.recv(1024)
        except Exception as e:
            flushed = 1
    if args.tm > 500:
        conn.settimeout((args.tm/1000)*3)  # for long periodic shots
    run_cmd_str = 'upycmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, targ, password)
    run_live_cmd = shlex.split(run_cmd_str)
    subprocess.call(run_live_cmd)
    time.sleep(0.5)
    t0 = time.time()
    test_val = []
    print(('{:^15}'*len(variables)).format(*variables))
    try:
        if log:
            while True:
                try:
                    data_acc = conn.recv(nb)
                    decode = dict(zip(variables,
                                      [struct.unpack(r_format, data_acc)]))
                    # print(decode)
                    for chunk in decode.values():
                        value = sum([val for val in chunk])/len(chunk)
                        sys.stdout.write("\033[K")
                        decode_p = dict(zip(variables,
                                            [format(value, '.4f')]))
                        print(('{:^15}'*len(decode_p.values())
                               ).format(*decode_p.values()), end='\r')
                        sys.stdout.flush()
                    with open(filename, 'a') as logfile:
                        logfile.write(json.dumps(decode))
                        logfile.write('\n')
                except Exception as e:
                    # print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break
        else:
            while True:
                try:
                    data_recv = conn.recv(nb)
                    decode = dict(zip(variables,
                                      [struct.unpack(r_format, data_recv)]))
                    # print(decode)
                    for chunk in decode.values():
                        value = sum([val for val in chunk])/len(chunk)
                        sys.stdout.write("\033[K")
                        decode_p = dict(zip(variables,
                                            [format(value, '.4f')]))
                        print(('{:^15}'*len(decode_p.values())
                               ).format(*decode_p.values()), end='\r')
                        sys.stdout.flush()
                    test_val.append(decode)
                    if t0 == 0:
                        t0 = time.time()

                    final_time = abs(time.time()-t0)
                except Exception as e:
                    # print(e)
                    if str(e) == 'timed out':
                        pass
                    else:
                        break

    except KeyboardInterrupt:
        try:
            print('\n')
            print('...closing...')
            conn.shutdown(socket.SHUT_RDWR)
            time.sleep(1)
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            print('Done!')
            print('TEST RESULTS ARE:')
            print('TEST DURATION : {} (s)'.format(final_time))
            # # FIND SAMPLING RATE
            # Method 1:
            N_DATA_PACKETS = len(test_val)  # Number of batches received
            print('DATA PACKETS : {} packets'.format(N_DATA_PACKETS))
            # (assuming al batches = buffer_size long)
            Fs = ((N_DATA_PACKETS+1)*BUFFERSIZE/final_time)
            print('SAMPLES PER PACKET : {}'.format(BUFFERSIZE))
            print('VARIABLES PER SAMPLE : {}; {}'.format(
                len(variables), variables))
            print('SIZE OF PACKETS: {} bytes'.format(nb))
            #  32 is batch/buffer size ,
            # so total samples is n_batches (len(vals)) x size_batch
            # print('Period: {} ms ; Fs:{} Hz'.format(
            # timestamp[:].mean()/1e3,1/((timestamp[:].mean())/1e6))
            conn.close()
            print('Period: {} ms ; Fs:{} Hz, Data send rate: {} packets/s of {} samples'.format(
                round((1/Fs)*1e3), round(Fs, -1),
                round(N_DATA_PACKETS/final_time), BUFFERSIZE))
            print(
                'DATA TRANSFER RATE: {} KB/s'.format(round(N_DATA_PACKETS/final_time)*nb/1024))
        except KeyboardInterrupt:
            conn.shutdown(socket.SHUT_RDWR)
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = '{}.stop_send();gc.collect()'.format(sensorlib)
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, targ, password)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
            conn.close()
            print('Done!')


def simple_cmd_r(cmd):
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    resp_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    resp_info_list.append(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(resp_info_list) > 0:
        return resp_info_list[0]


def print_sizefile(file_name, filesize, tabs=0):
    _kB = 1024
    if filesize < _kB:
        sizestr = str(filesize) + " by"
    elif filesize < _kB**2:
        sizestr = "%0.1f KB" % (filesize / _kB)
    elif filesize < _kB**3:
        sizestr = "%0.1f MB" % (filesize / _kB**2)
    else:
        sizestr = "%0.1f GB" % (filesize / _kB**3)

    prettyprintname = ""
    for _ in range(tabs):
        prettyprintname += "   "
    prettyprintname += file_name
    print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))


def print_sizefile_all(fileslist, tabs=0, frep=None):
    for filedata in fileslist:
        namefile = filedata[0]
        filesize = filedata[1]

        _kB = 1024
        if filesize < _kB:
            sizestr = str(filesize) + " by"
        elif filesize < _kB**2:
            sizestr = "%0.1f KB" % (filesize / _kB)
        elif filesize < _kB**3:
            sizestr = "%0.1f MB" % (filesize / _kB**2)
        else:
            sizestr = "%0.1f GB" % (filesize / _kB**3)

        prettyprintname = ""
        for _ in range(tabs):
            prettyprintname += "   "
        prettyprintname += namefile
        print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))
        if frep is not None:
            frep.append('{0:<40} Size: {1:>10}'.format(
                prettyprintname, sizestr))


def print_filesys_info(filesize):
    _kB = 1024
    if filesize < _kB:
        sizestr = str(filesize) + " by"
    elif filesize < _kB**2:
        sizestr = "%0.1f KB" % (filesize / _kB)
    elif filesize < _kB**3:
        sizestr = "%0.1f MB" % (filesize / _kB**2)
    else:
        sizestr = "%0.1f GB" % (filesize / _kB**3)
    return sizestr


# LOGGING DATETIME NOW:
def lognow(filename, sensor):
    if filename == 'now':
        return 'log_{}_{}.txt'.format(sensor,
                                      datetime.now().strftime("%m_%d_%Y_%H_%M_%S"))
    else:
        return filename

# CRYPTO


def rsa_keygen(dir='', store=True, show_key=False, id='00'):
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=args.key_size,
        backend=default_backend())
    my_p = getpass.getpass(prompt='Password: ', stream=None)
    pem = private_key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8,
                                    encryption_algorithm=serialization.BestAvailableEncryption(bytes(my_p, 'utf-8')))
    if show_key:
        print(pem)
    if store:
        with open(dir+'upy_pub_rsa{}.key'.format(id), 'wb') as keyfile:
            keyfile.write(pem)
    return pem


def load_rsa_key(dir='', show_key=False, id='00'):
    buff_key = b''
    try:
        with open(dir+'upy_pub_rsa{}.key'.format(id), 'rb') as keyfile:
            while True:
                try:
                    buff = keyfile.read(2000)
                    if buff != b'':
                        buff_key += buff
                    else:
                        break
                except Exception as e:
                    print(e)
        if show_key:
            print(buff_key)
        return buff_key
    except Exception as e:
        print("No RSA key found for this device, generate one first with '$ upydev gen_rsakey -tfkey' ")


def upy_keygen(rsa_key):
    aZ09 = bytes(string.ascii_letters + string.digits, 'ascii')
    raw_key_list = [line for line in rsa_key.splitlines()[1:-1]]
    raw_key = b''
    for line in raw_key_list:
        raw_key += line
    random_token = secrets.token_bytes(32)  # send this
    for b in random_token:
        raw_key += bytes(chr(raw_key[b]), 'utf-8')
    key_hash = hashlib.sha256()
    key_hash.update(raw_key)
    hashed_key = key_hash.digest()
    index_key = [secrets.randbelow(len(hashed_key)) for i in range(8)]  # send this
    password_long = bytes([hashed_key[val] for val in index_key])
    password_short = bytes([aZ09[val % len(aZ09)] for val in password_long]).decode()

    return (password_short, random_token + bytes(index_key))


def get_cert_data():
    # MAC ADDRES IF
    try:
        addrs = [netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] for
                    iface in netifaces.interfaces() if netifaces.AF_INET in
                    netifaces.ifaddresses(iface)][-1]

        USER = os.environ['USER']
        HOST_NAME = socket.gethostname()
        COUNTRY_CODE = locale.getlocale()[0].split('_')[1]
        return {'addrs': addrs, 'USER': USER, 'HOST_NAME': HOST_NAME,
                'COUNTRY_CODE': COUNTRY_CODE}
    except Exception as e:
        default_temp = {'addrs': 'xxxx', 'USER': 'xxxx', 'HOST_NAME': 'xxxx',
                        'COUNTRY_CODE': 'XX'}
        return default_temp


def ssl_ECDSA_key_certgen(ip, passwd, dir='', store=True):
    # print('Generating RSA key and Cerficate...')
    print('Getting unique id...')
    espdev = W_UPYDEVICE(ip, passwd)
    espdev.open_wconn()
    espdev.wr_cmd("from machine import unique_id; unique_id()",
                  silent=True, follow=False)
    unique_id = hexlify(espdev.output).decode()
    print('ID: {}'.format(unique_id))
    espdev.wr_cmd("import sys; sys.platform",
                  silent=True)
    dev_platform = espdev.output
    espdev.close_wconn()
    # key = rsa.generate_private_key(
    #     public_exponent=65537,
    #     key_size=args.key_size,
    #     backend=default_backend())
    key = ec.generate_private_key(
        ec.SECP256R1(),
        backend=default_backend())

    my_p = getpass.getpass(prompt='Passphrase: ', stream=None)
    pem = key.private_bytes(encoding=serialization.Encoding.PEM,
                            format=serialization.PrivateFormat.TraditionalOpenSSL,
                            encryption_algorithm=serialization.BestAvailableEncryption(bytes(my_p, 'utf-8')))

    if store:
        with open(dir+'SSL_key{}.pem'.format(unique_id), 'wb') as keyfile:
            keyfile.write(pem)
    cert_data = get_cert_data()
    subject = issuer = x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u"{}".format(cert_data['COUNTRY_CODE'])),
                                  x509.NameAttribute(NameOID.USER_ID, u"{}".format(cert_data['USER'])),
                                  x509.NameAttribute(NameOID.SURNAME, u"{}".format(cert_data['HOST_NAME'])),
                                  x509.NameAttribute(NameOID.STREET_ADDRESS, u"{}".format(cert_data['addrs'])),
                                  x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"MicroPython"),
                                  x509.NameAttribute(NameOID.COMMON_NAME, u"{}@{}".format(dev_platform, unique_id))])
    host_ip = cert_data['addrs']
    cert = x509.CertificateBuilder().subject_name(
                subject).issuer_name(issuer).public_key(key.public_key()
                ).serial_number(x509.random_serial_number()
                ).not_valid_before(datetime.utcnow()
                ).not_valid_after(datetime.utcnow() + timedelta(days=365)
                ).add_extension(x509.SubjectAlternativeName([x509.DNSName(u"localhost"),
                                                            x509.DNSName(u"{}".format(host_ip)),
                                                            x509.DNSName(u"wss://{}:8833".format(args.t)),
                                                            x509.DNSName(u"wss://192.168.4.1:8833")]),
                                                            critical=False
                ).sign(key, hashes.SHA256(), default_backend())
    if store:
        with open(dir+'SSL_certificate{}.pem'.format(unique_id), 'wb') as certfile:
            certfile.write(cert.public_bytes(serialization.Encoding.PEM))

    # CONVERT TO DER FORMAT
    cert_args = shlex.split('openssl x509 -in {}SSL_certificate{}.pem -out {}SSL_certificate{}.der -outform DER'.format(dir, unique_id, dir, unique_id))
    subprocess.call(cert_args)
    key_args = shlex.split('openssl ec -in {}SSL_key{}.pem -out {}SSL_key{}.der -outform DER'.format(dir, unique_id, dir, unique_id))
    subprocess.call(key_args)

    if args.tfkey:
        print('Transfering ECDSA key and Certificates to the device...')
        args.f = '{}/{}'.format(upydev.__path__[0], 'SSL_key{}.der'.format(unique_id))
        put_f(ip, passwd, rst=False)
        args.f = '{}/{}'.format(upydev.__path__[0], 'SSL_certificate{}.der'.format(unique_id))
        put_f(ip, passwd, rst=args.rst)

#############################################

# helparg = '''Mode:
# - config : to save upy device settings (see -p, -t, -g),
#             so the target and password arguments wont be required any more
# - put : to upload a file to upy device (see -f, -s, -fre, -dir, -rst)
# - get : to download a file from upy device (see -f, -s, -fre, -dir)
# - sync : for a faster transfer of large files
#     (this needs sync_tool.py in upy device) (see -f, -s and -lh)
# - d_sync: to recursively sync a folder in upydevice filesystem use -dir
#             to indicate the folder (must be in cwd), use -tree to see dir
#             structure, to sync to an Sd card mounted as 'sd' use -s sd
# - cmd : to send command to upy device ; (see -c, -r, -rl);
#         example: upydev cmd -c "led.on()" ; upydev cmd -r "print('Hello uPy')";
#                 upydev cmd -rl "function_that_print_multiple_lines()";
#     * tip: simple commands can be used without quotes;
#         but for commands with parenthesis or special characters use quotes,
#         for example: 'dummy_func()' ; use double quotes "" when the command
#         includes a string like this example: "uos.listdir('/sd')"
# - wrepl : to enter the terminal WebREPL; CTRL-x to exit, CTRL-d to do soft reset
#         To see more keybindings info do CTRL-k
#         (Added custom keybindings and autocompletion on tab to the previous work
#         see: https://github.com/Hermann-SW/webrepl for the original work)
# - wssrepl : to enter the terminal WebSecureREPL; CTRL-x to exit, CTRL-d to do soft reset
#         To see more keybindings info do CTRL-k. REPL over WebSecureSockets (This needs use of
#         'sslgen_key -tfkey', 'update_upyutils' and enable WebSecureREPL in the device "import wss_repl;wss_repl.start(ssl=True)")
# - srepl : to enter the terminal serial repl using picocom, indicate port by -port option
#         (to exit do CTRL-a, CTRL-x)
# - ping : pings the target to see if it is reachable, CTRL-C to stop \n
# - run : just calls import 'script', where 'script' is indicated by -f option
#         (script must be in upydevice or in sd card indicated by -s option
#         and the sd card must be already mounted as 'sd');
#         * Supports CTRL-C to stop the execution and exits nicely.
# - install : install libs to '/lib' path with upip; indicate lib with -f option
# - mpyx : to froze a module/script indicated with -f option, and save some RAM,
#          it uses mpy-cross tool (see https://gitlab.com/alelec/mpy_cross)
# - timeit: to measure execution time of a module/script indicated with -f option.
#           This is an implementation of
#           https://github.com/peterhinch/micropython-samples/tree/master/timed_function
# - fw: to list or get available firmware versions, use -md option to indicate operation:
#         to list do: "upydev fw -md list -b [BOARD]" board can be e.g. 'esp32','esp8266' or 'PYBD'
#                     "upydev fw -md list latest -b [BOARD]" to see the latest firmware available
#         to get do: "upydev fw -md get [firmware file]" or "upydev fw -md get latest -b[BOARD]"
#         * for list or get modes the -n option will filter the results further: e.g. -n ota
#         to see available serial ports do: "upydev fw -md list serial_ports"
# - flash: to flash a firmware file to the upydevice, a serial port must be indicated
#             to flash do: "upydev flash -port [serial port] -f [firmware file]"
# - see: to get specific command help info indicated with -c option. To get specific
#         info about a devices group use -G option as "see -G [GROUP NAME]"
# - find: to get a list of possible upy devices. Scans the local network to find devices
#         with port 8266 (WebREPL) open. Use -n option to perform n scans (A single scan
#         may not find all the devices)
# - diagnose: to make a diagnostic test of the device (sends useful to commands
#             to get device state info), to save report to file see -rep, use -n to save
#             the report with a custom name (automatic name is "upyd_ID_DATETIME.txt")
#             Use "-md local" option if connected to esp AP.
# - errlog: if 'error.log' is present in the upydevice, this shows the content
#             (cat('error.log')), if 'error.log' in sd use -s sd
# - stream_test: to test download speed (from device to host). Default test is 10 MB of
#                random bytes are sent in chunks of 20 kB and received in chunks of 32 kB.
#                To change test parameters use -chunk_tx , -chunk_rx, and -total_size.
# - sysctl : to start/stop a script without following the output. To follow initiate
#            wrepl/srepl as normal, and exit with CTRL-x (webrepl) or CTRL-A,X (srepl)
#            TO START: use -start [SCRIPT_NAME], TO STOP: use -stop [SCRIPT_NAME]
# - log: to log the output of a upydevice script, indicate script with -f option, and
#         the sys.stdout log level and file log level with -dslev and -dflev (defaults
#         are debug for sys.stdout and error for file). To log in background use -daemon
#         option, then the log will be redirected to a file with level -dslev.
#         To stop the 'daemon' log mode use -stopd and indicate script with -f option.
#         'Normal' file log and 'Daemon' file log are under .upydev_logs folder in $HOME
#         directory, named after the name of the script. To follow an on going 'daemon'
#         mode log, use -follow option and indicate the script with -f option.
# - update_upyutils: to update the latest versions of sync_tool.py, upylog.py,
#                 upynotify.py, upysecrets.py, upysh2.py, ssl_repl.py, uping.py, time_it.py,
#                 wss_repl.py and wss_helper.py.
# - debug: to execute a local script line by line in the target upydevice, use -f option
#         to indicate the file. To enter next line press ENTER, to finish PRESS C
#         then ENTER. To break a while loop do CTRL+C.
#
# - gen_rsakey: To generate RSA-2048 bit key that will be shared with the device
#               (it is unique for each device) use -tfkey to send this key to the
#               device (use only if connected directly to the AP of the device or a
#               "secure" wifi e.g. local/home). If not connected to a "secure" wifi
#               upload the key (it is stored in upydev.__path__) by USB/Serial connection.
# - rf_wrkey: To "refresh" the WebREPL password with a new random password derivated from
#             the RSA key previously generated. A token then is sent to the device to generate
#             the same password from the RSA key previously uploaded. This won't leave
#             any clues in the TCP Websocekts packages of the current WebREPL password.
#             (Only the token will be visible; check this using wireshark)
#             (This needs upysecrets.py)
#
# - crypto_wrepl: To enter the terminal CryptoWebREPL a E2EE wrepl/shell terminal;
#              CTRL-x to exit, CTRL-u to toggle encryption mode (enabled by default)
#              To see more keybindings info do CTRL-k. By default resets after exit,
#              use -rkey option to refresh the WebREPL password with a new random password,
#              after exit.This passowrd will be stored in the working directory or in global directory with
#              -g option. (This mode needs upysecrets.py)
# - upy: to access crypto_wrepl in a 'ssh' style command to be used like e.g.:
#       "upydev upy@192.168.1.42" or if a device is stored in a global group called "UPY_G" (this
#        needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 192.168.1.42 myfoopass")
#        The device can be accessed as "upydev upy@foo_device" or redirect any command as e.g.
#        "upydev ping -@foo_device"
#
# - sslgen_key: (This needs openssl available in $PATH)
#                To generate ECDSA key and a self-signed certificate to enable SSL sockets
#                This needs a passphrase, that will be required every time the key is loaded.
#                Use -tfkey to upload this key to the device
#                (use only if connected directly to the AP of the device or a
#                "secure" wifi e.g. local/home). If not connected to a "secure" wifi
#                upload the key (it is stored in upydev.__path__) by USB/Serial connection.
#
# - ssl_wrepl: To enter the terminal SSLWebREPL a E2EE wrepl/shell terminal (SSL sockets);
#              CTRL-x to exit, CTRL-u to toggle encryption mode (enabled by default)
#              To see more keybindings info do CTRL-k. By default resets after exit,
#              use -rkey option to refresh the WebREPL password with a new random password,
#              after exit.This passowrd will be stored in the working directory or in global directory with
#              -g option. (This mode needs ssl_repl.py, upysecrets.py for -rfkey)
#              *(Use -nem option to use without encryption (for esp8266))
# - ssl: to access ssl_wrepl in a 'ssh' style command to be used like e.g.:
#       "upydev ssl@192.168.1.42" or if a device is stored in a global group called "UPY_G" (this
#        needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 192.168.1.42 myfoopass")
#        The device can be accessed as "upydev ssl@foo_device" or redirect any command as e.g.
#        "upydev ping -@foo_device". *(For esp8266 use the option -nem (no encryption mode))
#
# - sh_srepl: To enter the serial terminal SHELL-REPL; CTRL-x to exit,
#             To see more keybindings info do CTRL-k. By default resets after exit.
#             To configure a serial device use -t for baudrate and -p for serial port
#             To access without previous configuration: "sh_srepl -port [serial port] -b [baudrate]"
#             (default baudrate is 115200)
#             To access with previous configuration:
#                 - "sh_srepl" (if device configured in current working directory)
#                 - "sh_srepl -@ foo_device" (if foo_device is configured in global group 'UPY_G')
#
# - shr: to access the serial terminal SHELL-REPL in a 'ssh' style command to be used like e.g.:
#       "upydev shr@/dev/tty.usbmodem3370377430372" or if a device is stored in a global group called "UPY_G" (this
#        needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 115200 /dev/tty.usbmodem3370377430372")
#        The device can be accessed as "upydev shr@foo_device"
#
# - wssl: to access ssl_wrepl if WebSecureREPL is enabled in a 'ssh' style command to be used like e.g.:
#       "upydev wssl@192.168.1.42" or if a device is stored in a global group called "UPY_G" (this
#        needs to be created first doing e.g. "upydev make_group -g -f UPY_G -devs foo_device 192.168.1.42 myfoopass")
#        then the device can be accessed as "upydev wssl@foo_device"
# - set_wss: To toggle between WebSecureREPL and WebREPL, to enable WebSecureREPL do 'set_wss', to disable 'set_wss -wss'
#
# - make_group: to make a group of boards to send commands to. Use -f for the name
#               of the group and -devs option to indicate a name, ip and the
#               password of each board. (To store the group settings globally use -g option)
#
# - mg_group: to manage a group of boards to send commands to. Use -G for the name
#               of the group and -add option to add devices (indicate a name, ip and the
#               password of each board) or -rm to remove devices (indicated by name)
# * GROUP COMMAND MODE:
#         To send a command to multiple devices in a group (made with make_group
#         command) use -G option as -G [GROUP NAME]; to target specific devices
#         within a group add -devs option as -devs [DEV NAME] [DEV NAME] ...
#         (upydev will use local working directory configuration unless it does
#         not find any or manually indicated with -g option)
#         * This sends the command to one device at a time;
#         ** For parallel command execution use -GP option (instead of -G)
#
# upy Commands:
# > GENERAL
# - info : for upy device system info
# - id : for upy device unique id
# - upysh : to enable the upy shell in the upy device (then do 'upydev man' to
#         access upysh manual info)
# - reset : to do a soft reset in upy device
# - kbi : sends CTRL-C signal to stop an ongoing loop, to be able to access repl again
# - uhelp : just calls micropython help
# - umodules: just calls micropython help('modules')
# - mem_info : for upy device RAM memory info; call it once to check actual memory,
#             call it twice and it will free some memory
# - filesize : to get the size of file in root dir (default) or sd with '-s sd' option;
#             if no file name indicated with -f option, prints all files
# - filesys_info : to get memory info of the file system, (total capacity, free, used),
#             (default root dir, -s option to change)
# - netinfo : for upy device network info if station is enabled and connected to an AP
# - netinfot : same as netinfo but in table format
# - netscan : for upy device network scan
# - netstat_on : to enable STA
# - netstat_off : to disable STA
# - netstat_conn : to connect to an AP , must provide essid and password (see -wp)
# - netstat : STA state ; returns True if enabled, False if disabled
# - ap_on : to enable AP
# - ap_off : to disable AP
# - apstat : AP state ; returns True if enabled, False if disabled
# - apconfig : AP configuration of essid and password with authmode WPA/WPA2/PSK,
#             (see -ap), needs first that the AP is enabled (do 'upydev ap_on')
# - apscan : scan devices connected to AP; returns number of devices and mac address
# - i2c_config : to configure the i2c pins (see -i2c, defaults are SCL=22, SDA=23)
# - i2c_scan : to scan i2c devices (must config i2c pins first)
# - spi_config: to configure the spi pins (see -spi, defaults are SCK=5,MISO=19,MOSI=18,CS=21)
# - set_localtime: to pass host localtime and set upy device rtc
# - set_ntptime: to set rtc from server, (see -utc for time zone)
# - get_datetime: to get date and time (must be set first, see above commands)
#
# > WIFI UTILS (needs wifiutils.py in upydevice, see upyutils in upydev github repo)
# - wlan_init: Initiates wlan util (call this before the following commands)
# - wsta_config: Saves a "netowrk STA" configuration json file in upydevice, use with -wp option as -wp 'ssid' 'password'
# - wap_config: Saves a "netowrk AP" configuration json file in upydevice, use with -ap option as -ap 'ssid' 'password'
# - wsta_conn: Connects to the wlan configured with the command wsta_config
# - wap_conn: Enables the upydevice AP configured with the command wap_config
#
# > SD
# - sd_enable: to enable/disable the LDO 3.3V regulator that powers the SD module
#              use -po option to indicate the Pin.
# - sd_init: to initialize the sd card; (spi must be configured first)
#            create sd object and mounts as a filesystem, needs sdcard.py see
#            https://github.com/Carglglz/upydev/blob/master/DOCS/Documentation.md#sd_init
# - sd_deinit: to unmount sd card
# - sd_auto: experimental command, needs a sd module with sd detection pin
#            and the SD_AM.py script. Enable an Interrupt
#            with the sd detection pin, so it mounts the sd when is detected,
#            and unmount the sd card when is extracted. See more info in:
#            https://github.com/Carglglz/upydev/blob/master/DOCS/Documentation.md#sd_auto
# >INPUT
# Sensors:
#     * ADC:
#         ^ON BOARD ADCS:
#         - adc_config : to config analog pin to read from (see pinout, -po and -att)
#         - aread : to read from an analog pin previously configured
#
#         ^EXTERNAL ADC: (I2C) ADS1115 ***
#         - ads_init : to initialize and configure ADS1115 and the channel to
#                     read from (see -ads, -ch)
#         - ads_read : to read from analog pin previously configured
#                 (see -tm option for stream mode, and -f for logging*)
#                 * for one shot read, logging is also available with -f and
#                     -n option (for tagging)
#                 * use '-f now' for automatic 'log_mode_datetime.txt' name.
#                 * for stream mode profiling use -tm [ms] -ads test
#     * IMU:
#         - imu_init : initialize IMU, use -imu option to indicate the imu library.
#                     (default option is 'lsm9ds1', see sensor requirements for more info')
#         - imuacc : one shot read of the IMU lineal accelerometer (g=-9.8m/s^2),
#                 (see -tm option for stream mode, and -f for logging*)
#                 * for one shot read, logging is also available with -f and
#                     -n option (for tagging)
#                 * use '-f now' for automatic 'log_mode_datetime.txt' name.
#                 * for stream mode profiling use -tm [ms] -imu test
#                 ** stream mode and logging are supported in imugy and imumag also
#         - imuacc_sd: log the imuacc data to the sd (must be mounted)
#                     with the file format 'log_mode_datetime.txt'
#                     (see -tm option for stream mode)
#         - imugy :  one shot read of the IMU gyroscope (deg/s)
#         - imumag : one shot read of the IMU magnetometer (gauss)
#     * WEATHER SENSOR: (BME280)
#         - bme_init: initialize bme, use -bme option to indicate the weather sensor library.
#                     (default option is 'bme280', see sensor requirements for more info')
#         - bme_read : to read values from bme (Temp(C), Pressure(Pa), Rel.Hummidity (percentage))
#                 (see -tm option for stream mode, and -f for logging*)
#                 * for one shot read, logging is also available with -f and
#                     -n option (for tagging)
#                 * use '-f now' for automatic 'log_mode_datetime.txt' name.
#                 * for stream mode profiling use -tm [ms] -bme test
#     * POWER SENSOR: (INA219)
#         - ina_init: initialize ina, use -ina option to indicate the power sensor library.
#                     (default option is 'ina219', see sensor requirements for more info')
#         - ina_read : to read values from ina (Pot.Diff (Volts), Current(mA), Power(mW))
#                 (see -tm option for stream mode, and -f for logging*)
#                 * for one shot read, logging is also available with -f and
#                     -n option (for tagging)
#                 * use '-f now' for automatic 'log_mode_datetime.txt' name.
#                 * for stream mode profiling use -tm [ms] -ina test
#         - ina_batt: Use the sensor to profile battery usage and estimate battery life left.
#                     It will made 100 measurements during 5 seconds. Indicate battery capacity
#                     with -batt option; (in mAh)
#
# >OUTPUT:
#     * DAC:
#         - dac_config: to config analog pin to write to (use -po option)
#         - dac_write: to write a value in volts (0-3.3V)
#         - dac_sig: to write a signal use -sig for different options:
#                   > [type] [Amp] [frequency]
#                     (type: 'sin, sq'; Amp 0-1 V ; fq:0-50 (above that fq loses quality))
#                   > start : starts signal generation
#                   > stop : stops signal
#                   > mod [Amp] [frequency]: modify the signal with the Amp and fq indicated.
#     * BUZZER:
#         - buzz_config: to config PWM pin to drive the buzzer (use -po option)
#         - buzz_set_alarm: to set an alarm at time indicated with option -at, be
#                          aware that the rtc time must be set first with set_localtime
#                          or set_ntptime
#         - buzz_interrupt: to configure an interrupt with pins indicated with -po,
#                         use -md 'rev' for interrupt reverse operation
#         - buzz_beep: make the buzzer beep, with options set by -opt,
#                     usage: buzz_beep -opt [beep_ms] [number_of_beeps] [time_between_beeps] [fq]
#     * DC MOTOR:
#         - dcmotor_config: to config PWM pins to drive a DC motor (use -po option as -po [DIR1] [DIR2])
#         - dcmotor_move: to move the motor to one direction ['R'] or the opposite ['L']
#                         use -to option as -to ['R' or 'L'] [VELOCITY] (60-512)
#         - dcmotor_stop: to stop the DC motor
#     * SERVO:
#         - servo_config: to configure the servo pin with -po option
#         - servo_angle: to move the servo an angle indicated by -opt option
#     * STEPPER MOTOR:
#         - stepper_config: to configure the step and direction pin with -po option
#                             *( -po [DIR_PIN] [STEP_PIN])
#         - stepper_move: to move the stepper to right or left, at a velocity and
#                        a numbers of steps indicated with -to option: [R or L] [velocity] [# steps]
#                        R: right, L:left, velocity (1000-20000) (smaller is faster) and
#                        # steps (int), where 200 steps means a complete lap
#
# NETWORKING:
#     * MQTT:
#         - mqtt_config: to set id, broker address, user and password, use with -client option
#                        as "mqtt_config -client [ID] [BROKER ADDRESS] [USER] [PASSWORD]"
#         - mqtt_conn: to start a mqtt client and connect to broker; use mqtt_config first
#         - mqtt_sub: to subscribe to a topic, use -to option as "mqtt_sub -to [TOPIC]"
#         - mqtt_pub: to publish to a topic, use -to option as "mqtt_pub -to [TOPIC] [PAYLOAD]" or
#                     "mqtt_pub -to [PAYLOAD]" if already subscribed to a topic.
#         - mqtt_check: to check for new messages of the subscribed topics.
#     * SOCKETS:
#         - socli_init: to initiate a socket client use with -server option as
#                       "socli_init -server [IP] [PORT] [BUFFER LENGTH]"
#         - socli_conn: to connect the socket client to a server (inidcated by IP)
#         - socli_send: to send a message to the server, use -n option to indicate
#                       the message
#         - socli_recv: to receive a message from the server
#         - socli_close: to close the client socket
#         - sosrv_init: to initiate a socket server, use with -server option as
#                       "sosrv_init -server [PORT] [BUFFER LENGTH]"
#         - sosrv_start: to start the server, waits for a connection
#         - sosrv_send: to send a message to the client, use -n option to indicate
#                       the message
#         - sosrv_recv: to receive a message from the client
#         - sosrv_close: to close the server socket
#     * UREQUEST:
#         - rget_json: to make a request to API that returns a JSON response format
#                     (indicate API URL with -f option)
#         - rget_text: to make a request to API that returns a text response format
#                     (indicate API URL with -f option)
#
# Port/board specific commands:
# - battery : if running on battery, gets battery voltage (esp32 huzzah feather)
# - pinout : to see the pinout reference/info of a board, indicated by -b option,
#            to request a single or a list of pins info use -po option
# - specs : to see the board specs, indicated by -b option
# - pin_status: to see pin state, to request a specific set use -po option ***
#
# * ESP32: (Not implemented yet)
#     - touch
#     - hall
#     - deepsleep
#     - temp
# '''
#
# usag = """%(prog)s [Mode] [options] or %(prog)s [upy command] [options] \n
# This means that if the first argument is not a Mode keyword or a
# upy command keyword it assumes it is a 'raw' upy command to send to the upy device \n
# Needs webrepl enabled in upy device;
# see http://docs.micropython.org/en/latest/esp32/quickref.html#webrepl-web-browser-interactive-prompt"""
#
# # UPY MODE KEYWORDS AND COMMANDS
# keywords_mode = ['put', 'get', 'sync', 'cmd', 'config', 'info', 'netinfo',
#                  'netscan', 'i2c_scan', 'id', 'reset', 'upysh', 'wrepl',
#                  'battery', 'mem_info', 'ping', 'filesize', 'filesys_info',
#                  'uhelp', 'umodules', 'netinfot', 'netstat_on', 'netstat_off',
#                  'netstat', 'ap_on', 'ap_off', 'apstat', 'apscan', 'run',
#                  'apconfig', 'netstat_conn', 'i2c_config', 'imu_init',
#                  'imuacc', 'imugy', 'imumag', 'pinout', 'specs', 'install',
#                  'pin_status', 'adc_config', 'aread', 'set_ntptime',
#                  'get_datetime', 'set_localtime', 'imuacc_sd', 'ads_init',
#                  'ads_read', 'sd_enable', 'spi_config', 'sd_init', 'sd_deinit',
#                  'sd_auto', 'dac_config', 'dac_write', 'dac_sig',
#                  'buzz_config', 'buzz_set_alarm', 'buzz_interrupt',
#                  'buzz_beep', 'servo_config', 'servo_angle', 'stepper_config',
#                  'stepper_move', 'dcmotor_config', 'dcmotor_move',
#                  'dcmotor_stop', 'mpyx', 'timeit', 'mqtt_config', 'mqtt_conn',
#                  'mqtt_pub', 'mqtt_sub', 'mqtt_check', 'socli_init',
#                  'socli_conn', 'socli_send', 'socli_recv', 'socli_close',
#                  'sosrv_init', 'sosrv_start', 'sosrv_send', 'sosrv_recv',
#                  'sosrv_close', 'rget_json', 'rget_text', 'fw', 'flash',
#                  'see', 'bme_init', 'bme_read', 'ina_init', 'ina_read',
#                  'ina_batt', 'make_group', 'srepl', 'mg_group', 'find',
#                  'wlan_init', 'wsta_config', 'wap_config', 'wsta_conn',
#                  'wap_conn', 'kbi', 'diagnose', 'errlog', 'd_sync',
#                  'stream_test', 'sysctl', 'log', 'update_upyutils',
#                  'debug', 'gen_rsakey', 'rf_wrkey', 'crypto_wrepl', 'upy',
#                  'sslgen_key', 'ssl_wrepl', 'ssl', 'sh_srepl', 'shr',
#                  'wssl', 'wssrepl', 'set_wss']
#
# help_dv = "To point the command to a specific device saved in the global group"
# # ARG PARSER
# parser = argparse.ArgumentParser(prog='upydev',
#                                  description='Command line tool for wireless Micropython devices',
#                                  formatter_class=argparse.RawTextHelpFormatter,
#                                  usage=usag, prefix_chars='-')
# parser.version = 'upydev: 0.2.8'
# parser.add_argument(
#     "m", metavar='Mode', help=helparg).completer = ChoicesCompleter(keywords_mode)
# parser.add_argument("-@", help=help_dv, required=False).completer = ChoicesCompleter(see_global_devs())
# parser.add_argument("-p", help='password', required=False)
# parser.add_argument("-f", help='script or file name', required=False)
# parser.add_argument(
#     "-lh", help='local ip for sync mode if can not be automatically detected',
#     required=False)
# parser.add_argument(
#     "-t", help='target host for example : 192.168.1.40 or for AP 192.168.4.1',
#     required=False)
# parser.add_argument(
#     "-sec", help='to configure a device password with no stream trace',
#     required=False, default=False, action='store_true')
# parser.add_argument(
#     "-s", help='source dir in upy device, default is root dir (flash memory); sd for sd card source dir mounted as "/sd"', required=False)
# parser.add_argument(
#     "-dir", help='target dir in upy device, default is root dir (flash memory)', required=False)
# parser.add_argument(
#     "-tree", help='to see the tree structure of a the directory to sync', required=False, default=False, action='store_true')
# parser.add_argument(
#     "-c", help='command to send to upy device, do not wait for a response',
#     required=False, type=str).completer = ChoicesCompleter(keywords_mode)
# parser.add_argument(
#     "-r", help='command to send to upy device, waits for a short response (one line)', required=False)
# parser.add_argument(
#     "-rl", help='command to send to upy device, waits for a longer response (multiple lines), so it can catch tracebacks messages', required=False)
# parser.add_argument(
#     "-g", help='to store/read the configuration file globally, if there is no config file in working directory, \n it uses the global file',
#     required=False, default=False, action='store_true')
# parser.add_argument(
#     "-st", help='shows target ip if using config file', required=False, default=False, action='store_true')
# parser.add_argument(
#     "-rst", help='reset flag after put file operation, default true, "f" to disable', required=False)
# parser.add_argument(
#     "-ap", help='[essid] [password] to set AP name and password',
#     required=False, nargs=2)
# parser.add_argument(
#     "-wp", help='[essid] [password] to connect the STA to an AP',
#     required=False, nargs=2)
# parser.add_argument(
#     "-i2c", help='[SCL] [SDA] to config scl and sda i2c pins in upy device',
#     required=False, nargs=2, default=[22, 23], type=int)
# parser.add_argument(
#     "-spi", help='[SCk] [MISO] [MOSI] [CS] to config scl and sda i2c pins in upy device',
#     required=False, nargs=4, default=[5, 19, 18, 21], type=int)
# parser.add_argument(
#     "-b", help='[BOARD NAME] to request info of specs or of the pinouts',
#     required=False).completer = ChoicesCompleter(['esp32', 'esp8266'])
# parser.add_argument(
#     "-att",
#     help='[attenuation] for attenuate adc input, default is 11dB attenuation, do -att info for more info',
#     required=False, default=3)
# parser.add_argument(
#     "-tm",
#     help='[timeout] enable stream mode and indicates a timeout in milliseconds',
#     required=False, type=int)
# parser.add_argument(
#     "-po",
#     help='[pin] indicates the pin to request info',
#     required=False, nargs='+', type=int)
#
# parser.add_argument(
#     "-opt",
#     help='wildcard option to be used by several commands',
#     required=False, nargs='+', type=int)
#
# parser.add_argument(
#     "-n",
#     help='tag a log shot from a sensor or adc',
#     required=False)
# parser.add_argument("-wss",
#                     help='Use WebSocket Secure (not available for all commands), this needs WebSecureREPL enabled "wss_repl.start(ssl=True)"',
#                     default=False, action='store_true')
# parser.add_argument('-v', action='version')
# parser.add_argument('-start', help='To start a script with sysctl; use as -start [SCRIPT_NAME]',
#                     required=False)
# parser.add_argument('-stop', help='To start a script with sysctl; use as -stop [SCRIPT_NAME]',
#                     required=False)
# parser.add_argument('-dflev', help='debug file mode level, options [debug, info, warning, error, critical]; default=error', default='error').completer = ChoicesCompleter(log_levs)
# parser.add_argument('-dslev', help='debug sys.stdout mode level, options [debug, info, warning, error, critical]; default=debug', default='debug').completer = ChoicesCompleter(log_levs)
# parser.add_argument('-daemon', help='enable "daemon mode", uses nohup so this means running in background, output if any is redirected to [SCRIPT_NAME]_daemon.log', default=False, action='store_true')
# parser.add_argument('-stopd', help='To stop a log daemon script', default=False, action='store_true')
# parser.add_argument('-follow', help='To follow a daemon log script file, indicate script with -f option', default=False, action='store_true')
# parser.add_argument('-rep', help='to save the report in a text file',
#                     required=False, default=False, action='store_true')
# parser.add_argument('-apmd', help='set target to 192.168.4.1',
#                     required=False, default=False, action='store_true')
# parser.add_argument('-chunk_tx', help='chunk size of data packets in kB to send for wifi speed test',
#                     required=False, default=20, type=int)
# parser.add_argument('-chunk_rx', help='chunk size of data packets in kB to receive for wifi speed test',
#                     required=False, default=32, type=int)
# parser.add_argument('-total_size', help='total size of data packets in MB for wifi speed test',
#                     required=False, default=10, type=int)
# parser.add_argument('-show_key', help='show RSA key',
#                     required=False, default=False, action='store_true')
# parser.add_argument('-tfkey', help='transfer RSA/ECDSA key,ideally this should be done only if connected to the AP of the device',
#                     required=False, default=False, action='store_true')
# parser.add_argument('-key_size', help='Indicate RSA key length in bits (default is 2048)',
#                     required=False, default=2048, type=int)
# parser.add_argument('-rkey', help='To refresh password after WebREPL disconnection',
#                     required=False, default=False, action='store_true')
# parser.add_argument('-localkey_id', help='To refresh password manually of a current WebREPL connection',
#                     required=False)
# parser.add_argument('-nem', help='To initiate CryptoWebREPL in unencrypted mode only',
#                     required=False, default=False, action='store_true')
# parser.add_argument('-utc', help='utc zone for set_nptime command', type=int,
#                     default=0)
# parser.add_argument('-imu', help='to select the imu library',
#                     default='lsm9ds1')
# parser.add_argument('-ads', help='to select the ads library and config options',
#                     default='ads1115')
# parser.add_argument('-ch', help='to select the ads analog channel to read from',
#                     type=int, default=0)
# parser.add_argument('-bme', help='to select the bme library and config options',
#                     default='bme280')
# parser.add_argument('-ina', help='to select the ina library and config options',
#                     default='ina219')
# parser.add_argument('-batt', help='to indicate battery capacity',
#                     type=int, nargs='+')
# parser.add_argument('-sig', help='to indicate a value or type of signal to write',
#                     nargs='+')
# parser.add_argument('-at', help='[HOUR] [MINUTE] [SECONDS]',
#                     nargs='+', type=int, default=[0, 0, 0])
# parser.add_argument('-to', help='[DIRECTION] [VELOCITY] [STEPS]',
#                     nargs='+', default=['R', 2000, 100])
#
# parser.add_argument('-client', help='[ID] [BROKER ADDRESS] [USER] [PASSWORD]',
#                     nargs='+', default=[None, 'test.mosquitto.org'])
# parser.add_argument('-server', help='For Client Socket:[IP] [PORT] [BUFFER LENGTH]; Server Socket [PORT] [BUFFER LENGTH]',
#                     nargs='+')
# parser.add_argument('-md', help='for command suboptions',
#                     nargs='+').completer = ChoicesCompleter(['list', 'get', 'serial_ports', 'latest'])
# parser.add_argument(
#     '-port', help='serial port of the device to flash to').completer = ChoicesCompleter(serial_ports())
# parser.add_argument('-devs', help='to indicate the devices that will be part of a group, use as -devs [DEV_1] [IP_1] [PASS_1] [DEV_2]...',
#                     nargs='+')
# parser.add_argument('-add', help='to indicate the devices that will be added to a group, use as -add [DEV_1] [IP_1] [PASS_1] [DEV_2]...',
#                     nargs='+')
# parser.add_argument('-rm', help='to indicate the devices that will be removed from a group, use as -rm [DEV_1] [DEV_2]...',
#                     nargs='+')
# parser.add_argument(
#     '-G', help='to indicate the group of devices that the command is directed to').completer = ChoicesCompleter(see_groups())
# parser.add_argument('-fre', help='special option to put or get files from upy device, can be "cwd", an expresion to match or name of files',
#                     nargs='+')
# parser.add_argument(
#     '-GP', help='to indicate the group of devices that the command is directed to, for parallel command execution').completer = ChoicesCompleter(see_groups())
# argcomplete.autocomplete(parser)
# args = parser.parse_args()

# MODE FUNCTIONS


def put_f(ip, passwd, rst=None, abs_path=False):
    dev = W_UPYDEVICE(ip, passwd)
    source = ''
    if args.s == 'sd':
        source = args.s + '/'
    passwd = passwd
    file = args.f
    if args.dir is not None:
        source = args.dir + '/'
    file_in_upy = file.split('/')[-1]
    if abs_path:
        file_in_upy = file
    host = ip
    print('Uploading file {}...'.format(file_in_upy))
    if not args.wss:
        copyfile_str = 'upytool {} {}:/{}{} -p {}'.format(
            file, host, source, file_in_upy, passwd)
    else:
        dev = BASE_WS_DEVICE(ip, passwd)
        copyfile_str = 'upytool {} {}:/{}{} -p {} -wss'.format(
            file, host, source, file_in_upy, passwd)
    # reset_str = 'upycmd -c "{}" -t {} -p {}'.format('D', host, passwd)
    subprocess.call(shlex.split(copyfile_str))

    print('File Uploaded!')
    time.sleep(3)
    if rst is None:
        print('Rebooting upy device...')
        if not args.wss:
            dev.reset(output=False)
        else:
            dev.reset(silent=True, ssl=True)
        print('Done!')
    else:
        pass


def put_multiple_f(ip, passwd, abs_path=False):
    # case 1: -fre is current working directory (cwd)
    rst = args.rst
    if args.fre[0] == 'cwd':
        print('Files in cwd to upload:')
        for file in os.listdir('./'):
            print(file)
        for file in os.listdir('./'):
            args.f = file
            put_f(ip, passwd, rst=False, abs_path=abs_path)
            print('\n')
        if rst is None:
            time.sleep(3)
            reset(ip, passwd)
    else:
        # case 2: -fre is an expression to match
        if len(args.fre) == 1:
            print("Files that match expression '{}' to upload:".format(
                args.fre[0]))
            for file in [fl for fl in os.listdir('./') if args.fre[0] in fl]:
                print(file)
            for file in [fl for fl in os.listdir('./') if args.fre[0] in fl]:
                args.f = file
                put_f(ip, passwd, rst=False, abs_path=abs_path)
                print('\n')
            if rst is None:
                time.sleep(3)
                reset(ip, passwd)
        else:
            # case 3: -fre is a list of files
            print('Files to upload:')
            for file in args.fre:
                print(file)
            for file in args.fre:
                args.f = file
                put_f(ip, passwd, rst=False, abs_path=abs_path)
                print('\n')
            if rst is None:
                time.sleep(3)
                reset(ip, passwd)


def get_f(ip, passwd, id_file='', check_file=True):
    if args.s is None and args.dir is None:
        print('Looking for file in upy device root dir')
        cmd_str = 'upycmd_r -c "uos.listdir()" -t {} -p {}'.format(ip, passwd)
        dir = 'root'
    if args.s == 'sd':
        print('Looking in SD memory...')
        cmd_str = 'upycmd_r -c "uos.listdir({})" -t {} -p {}'.format(
            str("'/sd'"), ip, passwd)
        dir = '/sd'
    if args.dir is not None:
        print('Looking for file in upy device {} dir'.format(args.dir))
        cmd_str = 'upycmd_r -c "uos.listdir({})" -t {} -p {}'.format(
            "'/{}'".format(args.dir), ip, passwd)
        dir = '/{}'.format(args.dir)
    resp = []
    if check_file:
        command = shlex.split(cmd_str)
        process = subprocess.Popen(command, stdout=subprocess.PIPE)
        time.sleep(0.1)
        stdout = process.communicate()
        try:
            resp = ast.literal_eval(
                stdout[0].decode('utf-8').split('\n')[0][4:-1])
        except Exception as e:
            try:
                resp = stdout[0].decode('utf-8').split('\n')[0]
            except Exception as e:
                resp = None

            pass

    try:
        if args.wss:
            check_file = False
        if args.f in resp or check_file is False:
            print('Getting file {}...'.format(args.f))
            file_to_get = args.f
            if args.s == 'sd':
                file_to_get = '/sd/{}'.format(args.f)
            if args.dir is not None:
                file_to_get = '/{}/{}'.format(args.dir, args.f)
            passwd = passwd
            target = ip
            if not args.wss:
                copyfile_str = 'upytool -p {} {}:{} .{}'.format(
                    passwd, target, file_to_get, id_file)
            else:
                copyfile_str = 'upytool -wss -p {} {}:{} .{}'.format(
                    passwd, target, file_to_get, id_file)

            subprocess.call(shlex.split(copyfile_str))
            print('Done!')
        else:
            if dir == 'root':
                print('File Not found in upy device {} directory'.format(dir))
            if dir == '/sd':
                print(
                    'File Not found in upy device {} directory or sd unmounted'.format(dir))
            else:
                print('File Not found in upy device {} directory'.format(dir))

    except Exception as e:
        print(e)
        pass


def get_files_list(ip, passwd):
    if args.s is None and args.dir is None:
        print('Looking for file in upy device root dir')
        cmd_str = 'upycmd_r -c "[file for file in uos.listdir() if not(uos.stat(file)[0] & 0x4000)]" -t {} -p {}'.format(ip, passwd)
        dir = 'root'
    if args.s == 'sd':
        print('Looking in SD memory...')
        cmd_str = 'upycmd_r -c "[file for file in uos.listdir({}) if not(uos.stat(file)[0] & 0x4000)]" -t {} -p {}'.format(
            str("'/sd'"), ip, passwd)
        dir = '/sd'
    if args.dir is not None:
        print('Looking for file in upy device {} dir'.format(args.dir))
        cmd_str = 'upycmd_r -c "[file for file in uos.listdir({}) if not(uos.stat(file)[0] & 0x4000)]" -t {} -p {}'.format(
            "'/{}'".format(args.dir), ip, passwd)
        dir = '/{}'.format(args.dir)

    command = shlex.split(cmd_str)
    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    time.sleep(0.1)
    stdout = process.communicate()
    try:
        resp = ast.literal_eval(
            stdout[0].decode('utf-8').split('\n')[0][4:-1])
        return resp
    except Exception as e:
        try:
            resp = stdout[0].decode('utf-8').split('\n')[0]
            return resp
        except Exception as e:
            resp = None
            return resp

        pass


def get_multiple_f(ip, passwd, id_file_dev='', device=None):
    # case 1: -fre is current working directory (cwd)
    file_list = ast.literal_eval(get_files_list(ip, passwd))
    if args.fre[0] == 'cwd':
        print('Files in upy device cwd to get:')
        for file in file_list:
            print(file)
        for file in file_list:
            args.f = file
            if id_file_dev != '':
                id_file_dev = '/{}_{}'.format(device, args.f)
            get_f(ip, passwd, id_file=id_file_dev, check_file=False)
            print('\n')
            time.sleep(0.5)
    else:
        # case 2: -fre is an expression to match
        if len(args.fre) == 1:
            print("Files in upy device that match expression '{}' to get:".format(
                args.fre[0]))
            for file in [fl for fl in file_list if args.fre[0] in fl]:
                print(file)
            for file in [fl for fl in file_list if args.fre[0] in fl]:
                args.f = file
                if id_file_dev != '':
                    id_file_dev = '/{}_{}'.format(device, args.f)
                get_f(ip, passwd, id_file=id_file_dev, check_file=False)
                print('\n')
                time.sleep(0.5)
        else:
            # case 3: -fre is a list of files
            print('Files to get:')
            for file in args.fre:
                print(file)
            for file in args.fre:
                args.f = file
                if id_file_dev != '':
                    id_file_dev = '/{}_{}'.format(device, args.f)
                get_f(ip, passwd, id_file=id_file_dev, check_file=False)
                print('\n')
                time.sleep(0.5)


def sync(ip, passwd, id_file=None, PORT=8005, resp=None, getfilelist=True):
    if getfilelist:
        espdev = W_UPYDEVICE(ip, passwd)
        espdev.open_wconn()
        if args.s is None:
            print('Looking for file in upy device root dir')
            cmd_str = "'{}' in uos.listdir()".format(args.f)
            dir = 'root'
        if args.s == 'sd':
            print('Looking in SD memory...')
            cmd_str = "'{}' in uos.listdir('/sd')".format(args.f)
            dir = '/sd'
        if getfilelist:
            if dir == '/sd':
                while espdev.output is None:
                    espdev.wr_cmd("'sd' in uos.listdir()", silent=True, follow=False)
                if espdev.output is True:
                    print('Found SD memory...')
                else:
                    print('SD not mounted, try to mount it first')
                    sys.exit()
            espdev.output = None
            while espdev.output is None:
                espdev.wr_cmd(cmd_str, silent=True, follow=False)
            found_file = espdev.output
            espdev.close_wconn()

    try:
        if resp is not None:
            found_file = True
        if found_file is True:
            target = ip
            print('Getting file {}...'.format(args.f))
            file_to_get = args.f
            copyfile = 'sync_server -f {} -t {} -p {} -port {}'.format(
                file_to_get, target, passwd, PORT)
            if id_file is not None:
                copyfile = 'sync_server -f {} -t {} -p {} -o {} -port {}'.format(
                    file_to_get, target, passwd, id_file, PORT)
            if args.lh is not None:
                copyfile = 'sync_server -f {} -t {} -p {} -lh {} -port {}'.format(
                    file_to_get, target, passwd, args.lh, PORT)
                if id_file is not None:
                    copyfile = 'sync_server -f {} -t {} -p {} -lh {} -o {} -port {}'.format(
                        file_to_get, target, passwd, args.lh, id_file, PORT)
            if args.s == 'sd':
                copyfile = 'sync_server -f {} -t {} -p {} -s {} -port {}'.format(
                    file_to_get, target, passwd, 'sd', PORT)
                if id_file is not None:
                    copyfile = 'sync_server -f {} -t {} -p {} -s {} -o {} -port {}'.format(
                        file_to_get, target, passwd, 'sd', id_file, PORT)
                if args.lh is not None:
                    copyfile = 'sync_server -f {} -t {} -p {} -s {} -lh {} -port {}'.format(
                        file_to_get, target, passwd, 'sd', args.lh, PORT)
                    if id_file is not None:
                        copyfile = 'sync_server -f {} -t {} -p {} -s {} -lh {} -o {} -port {}'.format(
                            file_to_get, target, passwd, 'sd', args.lh, id_file, PORT)

            # resp = run_command_rt(copyfile)
            copyfile_cmd = shlex.split(copyfile)
            subprocess.call(copyfile_cmd)
            # try:
            #     proc = subprocess.Popen(
            #         copyfile_cmd, stdout=subprocess.PIPE,
            #         stderr=subprocess.STDOUT)
            #     while proc.poll() is None:
            #         print(proc.stdout.readline()[:-1].decode())
            # except KeyboardInterrupt:
            #     time.sleep(1)
            #     result = proc.stdout.readlines()
            #     for message in result:
            #         print(message[:-1].decode())
        else:
            if dir == 'root':
                print('File Not found in upy device {} directory'.format(dir))
            if dir == '/sd':
                print(
                    'File Not found in upy device {} directory or sd unmounted'.format(dir))

    except Exception as e:
        print(e)
        pass


def sync_multiple_f(ip, passwd, id_file_dev=None, device=None, PORT_M=8005):
    # case 1: -fre is current working directory (cwd)
    file_list = ast.literal_eval(get_files_list(ip, passwd))
    if args.fre[0] == 'cwd':
        print('Files in upy device cwd to get:')
        for file in file_list:
            print(file)
        for file in file_list:
            args.f = file
            if id_file_dev is not None:
                id_file_dev = '{}_{}'.format(device, args.f)
            sync(ip, passwd, id_file=id_file_dev, PORT=PORT_M, resp=file_list,
                 getfilelist=False)
            print('\n')
    else:
        # case 2: -fre is an expression to match
        if len(args.fre) == 1:
            print("Files in that match expression '{}' to get:".format(
                args.fre[0]))
            for file in [fl for fl in file_list if args.fre[0] in fl]:
                print(file)
            for file in [fl for fl in file_list if args.fre[0] in fl]:
                args.f = file
                if id_file_dev is not None:
                    id_file_dev = '{}_{}'.format(device, args.f)
                sync(ip, passwd, id_file=id_file_dev, PORT=PORT_M,
                     resp=file_list, getfilelist=False)
                print('\n')
        else:
            # case 3: -fre is a list of files
            print('Files to get:')
            for file in args.fre:
                print(file)
            for file in args.fre:
                args.f = file
                if id_file_dev is not None:
                    id_file_dev = '{}_{}'.format(device, args.f)
                sync(ip, passwd, id_file=id_file_dev, PORT=PORT_M,
                     resp=file_list, getfilelist=False)
                print('\n')


# DIR_SYNC

def d_sync_recursive(folder, dev=None, rootdir='./', root_sync_folder=None,
                     show_tree=False):
    t0 = time.time()
    type_file_dict = {True:'<f>', False:'<d>'}
    if folder == root_sync_folder:
        print('DIRECTORY TO SYNC: {}'.format(folder))
        print('DIRECTORY SIZE: {} kB'.format(os.path.getsize(folder)/1024))
    if show_tree:
        print('DIRECTORY TREE STRUCTURE:\n')
        paths = DisplayablePath.make_tree(Path(folder))
        for path in paths:
            print(path.displayable())
    time.sleep(1)
    print('\n')
    print('***** {} *****'.format(folder))
    print('\n')
    directory = folder
    file_list = None
    file_list_abs_path = []
    dir_list_abs_path = []
    current_dir = directory
    # get directory structure:
    print('ROOT DIRECTORY: {}'.format(rootdir))
    print('DIRECTORY TO SYNC: {}'.format(directory))
    print('\n')
    print('CHECKING IF DIRECTORY {} IN: {}'.format(
        directory.split('/')[-1], rootdir))
    if directory.split('/')[-1] in os.listdir(rootdir):
        print('DIRECTORY {} FOUND'.format(directory))
        print('\n')
        print('FILES/DIRS IN DIRECTORY {}:'.format(directory))
        for file in os.listdir(directory):
            print('- {} {}'.format(type_file_dict[os.path.isfile(os.path.join(current_dir, file))],
                                   file))
        file_list = os.listdir(directory)
        print('\n')
        for file in file_list:
            if os.path.isfile(os.path.join(current_dir, file)):
                file_list_abs_path.append(os.path.join(current_dir, file))
            elif os.path.isdir(os.path.join(current_dir, file)):
                dir_list_abs_path.append(os.path.join(current_dir, file))
    print('LIST OF FILES TO UPLOAD:')
    for file in file_list_abs_path:
        print('- {}'.format(file.split('/')[-1]))
    print('\n')
    print('LIST OF SUBDIRS TO CREATE:')
    for subdir in dir_list_abs_path:
        print('- {}'.format(subdir.split('/')[-1]))
    print('\n')
    # Now create the root sync dir:
    if './' == rootdir:
        rootdir = ''
    if args.s == 'sd':
        rootdir = args.s + '/' + rootdir
    dev.d.output = None
    while dev.d.output is None:
        try:
            dev_root_list = dev.listdir(rootdir)
        except Exception as e:
            pass
    if args.s == 'sd':
        current_dir = args.s + '/' + current_dir
    if current_dir.split('/')[-1] not in dev_root_list:
    # print('uos = UOS(esp32); {} in uos.listdir("{}")'.format(
    #     current_dir.split('/')[-1], rootdir))
    # if dir not in root dir: create dir
        print('\n')
        print('MAKING DIR: {}'.format(current_dir))
        print('\n')
        dev.mkdir(current_dir)
        print('\n')
    print('UPLOADING FILES TO {}'.format(current_dir))
    # Now put files in dir: ** if dir_list_abs_path empty (len()==0) args.rst = True, else false
    # print('#upydev: args.fre = file_list_abs_path; put_multiple_f(args.t, args.p, abs_path=True)')
    # for file in file_list_abs_path:
    #     print('- {}'.format(file))
    files_names_too_long = []
    for file in file_list_abs_path:
        if len(file) > 64:
            print('\n')
            print('WARNING FILE NAME TOO LONG (>64 chr)')
            print('Using raw sync method instead...')
            print(file)
            files_names_too_long.append(file)
    print('\n')
    if len(files_names_too_long) > 0:
        dev.d.output = None
        while dev.d.output is None:
            dev.d.cmd('from sync_tool import sync_to_filesys; [1]', silent=True)
        print('RAW SYNC OF FILES:')
        print('\n')
    for file in files_names_too_long:
        print('- {}'.format(file))
    print('\n')
    for file in files_names_too_long:
        sync_to_dev(file, dev.d)
        time.sleep(2)
    for file in files_names_too_long:
        file_list_abs_path.remove(file)
    if len(file_list_abs_path) > 1:
        args.fre = file_list_abs_path
        args.rst = False
        put_multiple_f(args.t, args.p, abs_path=True)
    elif len(file_list_abs_path) == 1:
        args.f = file_list_abs_path[0]
        put_f(args.t, args.p, rst=False, abs_path=True)
    else:
        print('NO FILES IN DIR TO UPLOAD')
    # Now create subdirs:
    print('\n')
    print('MAKING SUBDIRS NOW...')
    for dir_ in dir_list_abs_path:
        print('\n')
        current_dir = dir_
        # Now create the root sync dir:
        # print('if not {} in uos.listdir("{}")'.format(
        #     current_dir.split('/')[-1], directory))
        # if dir not in root dir: create dir; else pass
        dev.d.output = None
        while dev.d.output is None:
            try:
                dir_to_sync = directory
                if args.s == 'sd':
                    dir_to_sync = args.s + '/' + directory
                dev_directory = dev.listdir(dir_to_sync)
            except Exception as e:
                pass
        if not current_dir.split('/')[-1] in dev_directory:
            if args.s == 'sd':
                current_dir = args.s + '/' + current_dir
            print('Creating dir: {}'.format(current_dir))
            # print(f'uos.mkdir("{current_dir}")')
            dev.mkdir(current_dir)
        else:
            print('DIRECTORY {} ALREADY EXISTS'.format(current_dir.split('/')[-1]))
    if len(dir_list_abs_path) == 0:
        print('NO MORE SUBDIRS')
    root = directory
    for dir_ in dir_list_abs_path:
        d_sync_recursive(dir_, dev, root)

    if directory == root_sync_folder:
        print('Done in : {:.2f} seconds'.format(time.time()-t0))
        # reset(args.t, args.p)
        if args.rst is None:
            print('Rebooting now...')
            time.sleep(2)
            dev.d.reset()


def ping(ip):
    ping_cmd_str = 'ping {}'.format(ip)
    ping_cmd = shlex.split(ping_cmd_str)
    try:
        proc = subprocess.Popen(
            ping_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        while proc.poll() is None:
            print(proc.stdout.readline()[:-1].decode())
    except KeyboardInterrupt:
        time.sleep(1)
        result = proc.stdout.readlines()
        for message in result:
            print(message[:-1].decode())


def ping_diagnose(ip, rep_file=None):
    ping_cmd_str = 'ping -c 5 {}'.format(ip)
    ping_cmd = shlex.split(ping_cmd_str)
    timeouts = 0
    try:
        proc = subprocess.Popen(
            ping_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            print(resp)
            if 'timeout' in resp:
                timeouts += 1
            if rep_file is not None:
                rep_file.append(resp)

        time.sleep(1)
        result = proc.stdout.readlines()
        for message in result:
            print(message[:-1].decode())
            rep_file.append(message[:-1].decode())

    except KeyboardInterrupt:
        time.sleep(1)
        result = proc.stdout.readlines()
        for message in result:
            print(message[:-1].decode())

    if timeouts >= 3:
        print('DEVICE IS DOWN OR SIGNAL RSSI IS TO LOW')
        print('WIRELESS DIAGNOSTICS NOT POSSIBLE, PLEASE RESET THE DEVICE OR PLACE IT NEARER TO THE LOCAL AP')
        return False
    else:
        return True


def run_script(ip, passwd, dev=''):
    dir = ''
    script_filepy = args.f
    script_name = script_filepy.split('.')[0]
    reload = "del(sys.modules['{}'])".format(script_name)
    run_cmd = "import {};".format(script_name)
    # print(run_cmd)
    if args.s is not None:
        dir = args.s
        sd_path = "import sys;sys.path.append('/{}')".format(dir)
        run_cmd = "{};import {}".format(
            sd_path, script_name)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, ip, passwd)
    # print(run_cmd_str)
    run_live_cmd = shlex.split(run_cmd_str)
    print('Running {}...'.format(args.f))
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print('{}{}'.format(dev, resp[4:]))
                else:
                    print('{}{}'.format(dev, resp))
            else:
                print(resp)
    except KeyboardInterrupt:
        try:
            print('...closing...')
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print(message[:-1].decode())
            for i in range(10):
                proc.stdout.readlines()

            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, ip, passwd)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
        except KeyboardInterrupt:
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, ip, passwd)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
    # Reload sys.modules and sys.path
    time.sleep(1)
    reload_cmd = "import sys;{};gc.collect()".format(reload)
    if args.s is not None:
        reload_syspath = "sys.path.remove('/{}')".format(dir)
        reload_cmd = "{};{};gc.collect()".format(reload, reload_syspath)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        reload_cmd, ip, passwd)
    reload_cmd_resp = run_command_rl(cmd_str)
    print('Done!')
    # sys.exit()


def timeit_script(ip, passwd, dev=''):
    timeit_import = "from time_it import tzero, tdiff, result;"
    dir = ''
    script_filepy = args.f
    script_name = script_filepy.split('.')[0]
    reload = "del(sys.modules['{}'])".format(script_name)
    timeit_cmd = "t_0 = tzero();import {};diff=tdiff(t_0);result('{}',diff);".format(
        script_name, script_name)
    timeit_final_cmd = timeit_import + timeit_cmd
    # print(run_cmd)
    if args.s is not None:
        dir = args.s
        sd_path = "import sys;sys.path.append('/{}')".format(dir)
        timeit_final_cmd = "{};{}".format(
            sd_path, timeit_final_cmd)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        timeit_final_cmd, ip, passwd)
    run_live_cmd = shlex.split(run_cmd_str)
    print('Running {}...'.format(args.f))
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print('{}{}'.format(dev, resp[4:]))
                else:
                    print('{}{}'.format(dev, resp))
            else:
                print(resp)
    except KeyboardInterrupt:
        try:
            print('...closing...')
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print(message[:-1].decode())
            for i in range(10):
                proc.stdout.readlines()
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, ip, passwd)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
        except KeyboardInterrupt:
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, ip, passwd)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
    # Reload sys.modules and sys.path
    time.sleep(1)
    reload_cmd = "import sys;{};gc.collect()".format(reload)
    if args.s is not None:
        reload_syspath = "sys.path.remove('/{}')".format(dir)
        reload_cmd = "{};{};gc.collect()".format(reload, reload_syspath)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        reload_cmd, ip, passwd)
    reload_cmd_resp = run_command_rl(cmd_str)
    print('Done!')


def install_f(ip, passwd, dev=''):
    lib = args.f
    install_cmd = "import upip;upip.install('{}')".format(lib)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        install_cmd, ip, passwd)
    run_live_cmd = shlex.split(run_cmd_str)
    print('{} Looking for {} lib...'.format(dev, args.f))
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print('{}{}'.format(dev, resp[4:]))
                else:
                    print('{}{}'.format(dev, resp))
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('{}Library {} installed!'.format(dev, args.f))


def see():
    if args.G is None:
        help_file = '{}/help.config'.format(upydev.__path__[0])
        with open(help_file, 'r') as helpref:
            help_dict = json.loads(helpref.read())
        columns, rows = os.get_terminal_size(0)
        print('\n'.join(textwrap.wrap(help_dict[args.c], columns-3)))
    else:
        group_file = '{}.config'.format(args.G)
        if group_file not in os.listdir() or args.g:
            group_file = '{}/{}.config'.format(upydev.__path__[0], args.G)
        with open(group_file, 'r') as group:
            group_devs = (json.loads(group.read()))
        print('GROUP NAME: {}'.format(args.G))
        print('# DEVICES: {}'.format(len(group_devs.keys())))
        for key in group_devs.keys():
            dev_ip = group_devs[key][0]
            if '.' in dev_ip:
                print('DEVICE NAME: {}, IP: {}'.format(key, group_devs[key][0]))
            else:
                print('DEVICE NAME: {}, SERIAL PORT: {}'.format(key, group_devs[key][1]))


def reset(ip, passwd):
    reset_str = 'upycmd -c "{}" -t {} -p {}'.format(
        'D', ip, passwd)
    print('Rebooting upy device...')
    command = shlex.split(reset_str)
    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    time.sleep(0.1)
    print('Done!')


def kbi(ip, passwd):
    reset_str = 'upycmd -c "{}" -t {} -p {}'.format(
        '0x3', ip, passwd)
    print('Keyboard Interrupt sent!')
    command = shlex.split(reset_str)
    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    time.sleep(0.1)
    print('Done!')


def wrepl(ip, passwd):
    web_repl_cmd_str = 'web_repl {} -p {}'.format(ip, passwd)
    web_repl_cmd = shlex.split(web_repl_cmd_str)
    try:
        web_repl = subprocess.call(web_repl_cmd)
    except KeyboardInterrupt:
        pass
        print('')
    if args.rkey:
        refresh_wrkey(ip, passwd)
        sys.exit()

def wssrepl(ip, passwd):
    web_repl_cmd_str = 'web_repl {} -p {} -wss'.format(ip, passwd)
    web_repl_cmd = shlex.split(web_repl_cmd_str)
    try:
        web_repl = subprocess.call(web_repl_cmd)
    except KeyboardInterrupt:
        pass
        print('')
    if args.rkey:
        refresh_wrkey(ip, passwd)
        sys.exit()


def srepl(serial_port):
    serial_repl_cmd_str = 'picocom {} -b115200'.format(serial_port)
    serial_repl_cmd = shlex.split(serial_repl_cmd_str)
    try:
        serial_repl = subprocess.call(serial_repl_cmd)
    except KeyboardInterrupt:
        pass
        print('')


def jupyterc():
    jupyter_cmd_str = 'jupyter console --kernel=micropython-upydevice'
    jupyter_cmd = shlex.split(jupyter_cmd_str)
    old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

    def preexec_function(action=old_action):
        signal.signal(signal.SIGINT, action)
    try:
        jupyter_console = subprocess.call(jupyter_cmd, preexec_fn=preexec_function)
        signal.signal(signal.SIGINT, old_action)
    except KeyboardInterrupt:
        pass
        print('')
    sys.exit()


def find_devices():
    print('Scanning WLAN {} for upy devices...'.format(get_ssid()))
    gws = netifaces.gateways()
    host_range = "{}-255".format(gws['default'][netifaces.AF_INET][0])
    nmScan = nmap.PortScanner()
    n_scans = 1
    if args.n is not None:
        n_scans = int(args.n)
    for i in range(n_scans):
        print('SCAN # {}'.format(i))
        my_scan = nmScan.scan(hosts=host_range, arguments='-p 8266')
        hosts_list = [{'host': x, 'state': nmScan[x]['status']['state'], 'port':list(
            nmScan[x]['tcp'].keys())[0], 'status':nmScan[x]['tcp'][8266]['state']} for x in nmScan.all_hosts()]
        devs = [host for host in hosts_list if host['status'] == 'open']
        print('FOUND {} device/s :'.format(len(devs)))
        N = 1
        for dev in devs:
            try:
                print('DEVICE {}: , IP: {} , STATE: {}, PORT: {}, STATUS: {}'.format(
                    N, dev['host'], dev['state'], dev['port'], dev['status']))
                N += 1
            except Exception as e:
                pass


#############################################
def diagnose(target, passwd, save_rep=True, rst_opt=True, name_rep=None, vers=None, local=False):
    t0 = time.time()
    esp32 = W_UPYDEVICE(target, passwd)

    FILE_REPORT = []
    # FULL REPORT TEST
    print('{:<10} {} {:>10}'.format('*'*20, 'uPydev Diagnostics Test', '*'*20))
    FILE_REPORT.append('{:<10} {} {:>10}'.format(
        '*'*20, 'uPydev Diagnostics Test', '*'*20))
    print('\n')
    FILE_REPORT.append('\n')
    print('upydev version : {}'.format(vers))
    FILE_REPORT.append('upydev version : {}'.format(vers))
    # CHECK IF DEVICE IS REACHABLE
    print('\n')
    FILE_REPORT.append('\n')
    print('{:<10} {} {:>10}'.format('='*20, 'PING TEST', '='*20))
    FILE_REPORT.append('{:<10} {} {:>10}'.format('='*20, 'PING TEST', '='*20))
    device_is_on = ping_diagnose(target, rep_file=FILE_REPORT)
    print('\n')
    FILE_REPORT.append('\n')
    # CHECK IF PORT 8266 IS OPEN(WEBREPL)
    host_range = target
    nmScan = nmap.PortScanner()
    print('{:<10} {} {:>10}'.format('='*20, 'NMAP TEST', '='*20))
    FILE_REPORT.append('{:<10} {} {:>10}'.format('='*20, 'NMAP TEST', '='*20))
    print('\n')
    FILE_REPORT.append('\n')
    n_tries = 0
    while n_tries < 3:
        my_scan = nmScan.scan(hosts=host_range, arguments='-p 8266 -Pn')
        hosts_list = [{'host': x, 'state': nmScan[x]['status']['state'], 'port': list(
            nmScan[x]['tcp'].keys())[0], 'status':nmScan[x]['tcp'][8266]['state']} for x in nmScan.all_hosts()]
        devs = [host for host in hosts_list if host['status'] == 'open']
        if len(devs) > 0:
            n_tries = 3
            print('DEVICE FOUND')
            FILE_REPORT.append('DEVICE FOUND')
            N = 1
            for dev in devs:
                try:
                    print('DEVICE {}: , IP: {} , STATE: {}, PORT: {}, STATUS: {}'.format(
                        N, dev['host'], dev['state'], dev['port'], dev['status']))
                    FILE_REPORT.append('DEVICE {}: , IP: {} , STATE: {}, PORT: {}, STATUS: {}'.format(
                        N, dev['host'], dev['state'], dev['port'], dev['status']))
                    N += 1
                except Exception as e:
                    pass
        else:
            if n_tries >= 2:
                print('DEVICE NOT FOUND')
                FILE_REPORT.append('DEVICE NOT FOUND')
                n_tries += 1
            else:
                n_tries += 1
    # ISSUE A KBI
    if device_is_on:
        esp32.kbi(output=False)
        while esp32.output is None:
            esp32.cmd('dir()', silent=True)
        glo_vars = esp32.output.copy()
        # ID
        print('\n')
        FILE_REPORT.append('\n')
        print('{:<10} {} {:>10}'.format('='*10, 'MACHINE ID', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'MACHINE ID', '='*10))
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(
                'import machine; machine.unique_id();gc.collect()', silent=True)
        print('\n')
        FILE_REPORT.append('\n')
        print("ID: {}".format(hexlify(esp32.output).decode()))
        upy_id = hexlify(esp32.output).decode()
        FILE_REPORT.append("ID: {}".format(hexlify(esp32.output).decode()))
        print('\n')
        FILE_REPORT.append('\n')
        # UNAME
        print('{:<10} {} {:>10}'.format('='*10, 'DEVICE INFO', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'DEVICE INFO', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.response = None
        while esp32.response is None:
            esp32.cmd('import uos; uos.uname()', silent=True)
        info = {data.split('=')[0].strip(): data.split('=')[1].replace(
            "'", '') for data in esp32.response.strip()[1:-1].split(',')}
        for key in info.keys():
            print('{}: {}'.format(key.capitalize(), info[key]))
            FILE_REPORT.append('{}: {}'.format(key.capitalize(), info[key]))
        print('\n')
        FILE_REPORT.append('\n')
        # CHECK AVAILABLE FIRMWARE
        if not local:
            print('{:<10} {} {:>10}'.format(
                '='*10, 'AVAILABLE FIRMWARE', '='*10))
            FILE_REPORT.append('{:<10} {} {:>10}'.format(
                '='*10, 'AVAILABLE FIRMWARE', '='*10))
            print('\n')
            FILE_REPORT.append('\n')
            print('Available firmware found for {}'.format(info['nodename']))
            FILE_REPORT.append(
                'Available firmware found for {}'.format(info['nodename']))
            for vers in get_fw_versions(info['nodename'])[1]:
                print('  - {}'.format(vers))
                FILE_REPORT.append('  - {}'.format(vers))
            print('\n')
            FILE_REPORT.append('\n')
        # RAM MEM
        print('{:<10} {} {:>10}'.format('='*10, 'RAM', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format('='*10, 'RAM', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.long_output = []
        while len(esp32.long_output) == 0:
            esp32.cmd('from micropython import mem_info;mem_info();gc.collect()',
                      silent=True, capture_output=True)
        for line in esp32.long_output:
            print(line)
            FILE_REPORT.append(line)
        mem = {elem.strip().split(':')[0]: int(elem.strip().split(
            ':')[1]) for elem in esp32.long_output[1][4:].strip().split(',')}
        print('RAM INFO:')
        FILE_REPORT.append('RAM INFO:')
        for key in mem.keys():
            print("  - {}: {} KB".format(key.capitalize(), mem[key]/1024))
            FILE_REPORT.append(
                "  - {}: {} KB".format(key.capitalize(), mem[key]/1024))
        print('\n')
        FILE_REPORT.append('\n')
        # DIR()
        print('{:<10} {} {:>10}'.format(
            '='*10, 'GLOBAL SPACE VARIABLES', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'GLOBAL SPACE VARIABLES', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        print(glo_vars)
        FILE_REPORT.append(glo_vars)
        print('\n')
        FILE_REPORT.append('\n')
        # FLASH MEM
        print('{:<10} {} {:>10}'.format('='*10, 'FLASH MEMORY', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'FLASH MEMORY', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd("import uos;uos.statvfs('');gc.collect()", silent=True)
        filesys_info = esp32.output
        size_info = filesys_info
        total_mem = print_filesys_info(size_info[0] * size_info[2])
        free_mem = print_filesys_info(size_info[0] * size_info[3])
        used_mem = print_filesys_info(
            (size_info[0] * size_info[2]) - (size_info[0] * size_info[3]))
        print('{} Memory'.format('FLASH'))
        FILE_REPORT.append('{} Memory'.format('FLASH'))
        print('TOTAL: {} , FREE: {} , USED: {}'.format(
            total_mem, free_mem, used_mem))
        FILE_REPORT.append('TOTAL: {} , FREE: {} , USED: {}'.format(
            total_mem, free_mem, used_mem))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd("import uos;uos.listdir('/')", silent=True)
        if 'sd' in esp32.output:
            sd_ismounted = True
            try:
                esp32.output = None
                while esp32.output is None:
                    esp32.cmd("import uos;uos.statvfs('/sd');gc.collect()",
                              silent=True, capture_output=True)
            except Exception as e:
                pass
            filesys_info = esp32.output
            size_info = filesys_info
            total_mem = print_filesys_info(size_info[0] * size_info[2])
            free_mem = print_filesys_info(size_info[0] * size_info[3])
            used_mem = print_filesys_info(
                (size_info[0] * size_info[2]) - (size_info[0] * size_info[3]))
            print('{} Memory'.format('SD'))
            FILE_REPORT.append('{} Memory'.format('SD'))
            print('TOTAL: {} , FREE: {} , USED: {}'.format(
                total_mem, free_mem, used_mem))
            FILE_REPORT.append('TOTAL: {} , FREE: {} , USED: {}'.format(
                total_mem, free_mem, used_mem))
            print('\n')
            FILE_REPORT.append('\n')
        else:
            sd_ismounted = False
            print('SD NOT FOUND')
            FILE_REPORT.append('SD NOT FOUND')
        # FILES IN FLASH + SIZE
        print('{:<10} {} {:>10}'.format(
            '='*10, 'Files in FLASH MEMORY', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'Files in FLASH MEMORY', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(
                "[(filename,uos.stat(''+str(filename))[6]) for filename in uos.listdir('')]", silent=True)
        filesize = esp32.output
        filesize.sort(key=sortSecond, reverse=True)
        print('Files in FLASH MEMORY:')
        FILE_REPORT.append('Files in FLASH MEMORY:')
        print_sizefile_all(filesize, frep=FILE_REPORT)
        print('\n')
        FILE_REPORT.append('\n')
        # MODULES
        print('{:<10} {} {:>10}'.format(
            '='*10, 'Frozen Modules in Firmware', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'Frozen Modules in Firmware', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.long_output = []
        while len(esp32.long_output) == 0:
            esp32.cmd("help('modules')", silent=True, capture_output=True)
        for module in esp32.long_output:
            print(module.strip())
            FILE_REPORT.append(module.strip())
        print('\n')
        FILE_REPORT.append('\n')
        # /LIB
        print('{:<10} {} {:>10}'.format('='*10, 'Modules in /lib', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'Modules in /lib', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd("import uos; uos.listdir('/lib')", silent=True)
        for flib in esp32.output:
            print(flib)
            FILE_REPORT.append(flib)
        print('\n')
        FILE_REPORT.append('\n')
        print('{:<10} {} {:>10}'.format(
            '='*10, 'Modules/scripts in /', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'Modules/scripts in /', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd("import uos; uos.listdir('/')", silent=True)
        pyfiles = [pyf for pyf in esp32.output if '.py' in pyf]
        for flib in pyfiles:
            print(flib)
            FILE_REPORT.append(flib)

        print('\n')
        FILE_REPORT.append('\n')

        # I2C SCAN (config option)
        print('{:<10} {} {:>10}'.format('='*10, 'I2C SCAN', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'I2C SCAN', '='*10))
        i2c_scl = "22"
        i2c_sda = "23"
        if info['sysname'] == 'esp8266':
            i2c_scl = "5"
            i2c_sda = "4"
        i2c_cmd = "from machine import I2C,Pin;i2c = I2C(scl=Pin({}),sda=Pin({}));i2c.scan();gc.collect()".format(
            i2c_scl, i2c_sda)
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(i2c_cmd, silent=True)
        i2c_devs = esp32.output
        print('Found {} i2c devices'.format(len(i2c_devs)))
        FILE_REPORT.append('Found {} i2c devices'.format(len(i2c_devs)))
        print('DEC: ')
        FILE_REPORT.append('DEC: ')
        print(i2c_devs)
        FILE_REPORT.append(i2c_devs)
        print('HEX:')
        FILE_REPORT.append('HEX:')
        print([hex(val) for val in i2c_devs])
        FILE_REPORT.append([hex(val) for val in i2c_devs])

        print('\n')
        FILE_REPORT.append('\n')
        # RTC CHECK LOCAL TIME - HOST TIME DIFF
        print('{:<10} {} {:>10}'.format('='*10, 'RTC DATETIME CHECK', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'RTC DATETIME CHECK', '='*10))
        get_time_cmd = "import time;tnow = time.localtime();"
        get_time2_cmd = "[tnow[0],tnow[1],tnow[2],tnow[3],tnow[4],tnow[5]];gc.collect()"
        get_datetime_cmd = get_time_cmd + get_time2_cmd
        esp32.response = None
        while esp32.response is None:
            esp32.cmd(get_datetime_cmd, silent=True)
        localtime = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
        print('\n')
        FILE_REPORT.append('\n')
        devicetime = "{}-{}-{}T{}:{}:{}".format(*_ft_datetime(esp32.output))
        print('{:<15} :  {:^15}'.format('Device DateTime', devicetime))
        FILE_REPORT.append('{:<15} :  {:^15}'.format(
            'Device DateTime', devicetime))
        print('{:<15} :  {:^15}'.format('Local DateTime', localtime))
        FILE_REPORT.append('{:<15} :  {:^15}'.format(
            'Local DateTime', localtime))
        print('\n')
        FILE_REPORT.append('\n')
        # IF NETWORK:
        is_conn_cmd = 'import network; network.WLAN(network.STA_IF).isconnected()'
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(is_conn_cmd, silent=True)
        is_connected = esp32.output
        # STA (NETINFOT)
        print('{:<10} {} {:>10}'.format(
            '='*10, 'NETWORK STA IF CONFIG', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'NETWORK STA IF CONFIG', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        if is_connected:
            is_connected = True
            net_cmd = "import network;network.WLAN(network.STA_IF).ifconfig();"
            net_info_cmd = "network.WLAN(network.STA_IF).config('essid');"
            net_signal_cmd = "network.WLAN(network.STA_IF).status('rssi');gc.collect()"
            net_comp_cmd = '{}{}{}'.format(
                net_cmd, net_info_cmd, net_signal_cmd)
            esp32.long_output = []
            while len(esp32.long_output) == 0:
                esp32.cmd(net_comp_cmd, silent=True, capture_output=True)
            net_info_list = esp32.long_output
            print('=' * 106)
            FILE_REPORT.append('=' * 106)
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
                'IP', 'SUBNET', 'GATEAWAY', 'DNS', 'ESSID', 'RSSI (dB)'))
            FILE_REPORT.append('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
                'IP', 'SUBNET', 'GATEAWAY', 'DNS', 'ESSID', 'RSSI (dB)'))
            try:
                print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
                    *ast.literal_eval(net_info_list[0]
                                      ), net_info_list[1].strip(),
                    net_info_list[2].strip()))
                FILE_REPORT.append('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
                    *ast.literal_eval(net_info_list[0]
                                      ), net_info_list[1].strip(),
                    net_info_list[2].strip()))
            except Exception as e:
                print(e)
                pass
        else:
            print('STA NOT CONNECTED')
            FILE_REPORT.append('STA NOT CONNECTED')
        # STA (NETSCAN)
        print('\n')
        FILE_REPORT.append('\n')
        print('{:<10} {} {:>10}'.format('='*10, 'NETWORK STA SCAN', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'NETWORK STA SCAN', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        net_cmd = "import network;network.WLAN(network.STA_IF).active(1);network.WLAN(network.STA_IF).scan();gc.collect()"
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(net_cmd, silent=True)
        net_scan_list = esp32.output
        print('=' * 110)
        FILE_REPORT.append('=' * 110)
        print('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
            'ESSID', 'BSSID', 'CHANNEL', 'RSSI (dB)', 'AUTHMODE', 'HIDDEN'))
        FILE_REPORT.append('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
            'ESSID', 'BSSID', 'CHANNEL', 'RSSI (dB)', 'AUTHMODE', 'HIDDEN'))
        print('=' * 110)
        FILE_REPORT.append('=' * 110)
        for netscan in net_scan_list:
            auth = AUTHMODE_DICT[netscan[4]]
            vals = hexlify(netscan[1]).decode()
            bssid = ':'.join([vals[i:i+2] for i in range(0, len(vals), 2)])
            print('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
                netscan[0].decode(), bssid , netscan[2], netscan[3],
                auth, str(netscan[5])))
            FILE_REPORT.append('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
                netscan[0].decode(), bssid , netscan[2], netscan[3],
                auth, str(netscan[5])))
        print('\n')
        FILE_REPORT.append('\n')
        # AP (APSTAT)
        esp32.output = None
        esp32.long_output = []
        print('{:<10} {} {:>10}'.format(
            '='*10, 'NETWORK AP IF CONFIG', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'NETWORK AP IF CONFIG', '='*10))
        ap_state = "network.WLAN(network.AP_IF).active()"
        ap_essid = "network.WLAN(network.AP_IF).config('essid')"
        ap_channel = "network.WLAN(network.AP_IF).config('channel')"
        ap_authmode = "network.WLAN(network.AP_IF).config('authmode')"
        ap_ifconfig = "network.WLAN(network.AP_IF).ifconfig()"
        apstat_cmd = "import network;{};{};{};{};{};gc.collect()".format(
            ap_state, ap_essid, ap_channel, ap_authmode, ap_ifconfig)
        while len(esp32.long_output) == 0:
            esp32.cmd(apstat_cmd, silent=True, capture_output=True)
        apstat_info = [val.strip() for val in esp32.long_output]
        auth = AUTHMODE_DICT[int(apstat_info[-2])]
        print('\n')
        FILE_REPORT.append('\n')
        print('=' * 70)
        FILE_REPORT.append('=' * 70)
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
            'AP ENABLED', 'ESSID', 'CHANNEL', 'AUTHMODE'))
        FILE_REPORT.append('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
            'AP ENABLED', 'ESSID', 'CHANNEL', 'AUTHMODE'))
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(
            *apstat_info[:-2], auth))
        FILE_REPORT.append('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(
            *apstat_info[:-2], auth))
        print('=' * 70)
        FILE_REPORT.append('=' * 70)
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
            'IP', 'SUBNET', 'GATEAWAY', 'DNS'))
        FILE_REPORT.append('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
            'IP', 'SUBNET', 'GATEAWAY', 'DNS'))
        try:
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(
                *ast.literal_eval(apstat_info[-1])))
            FILE_REPORT.append('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(
                *ast.literal_eval(apstat_info[-1])))
        except Exception as e:
            print(e)
            pass
        print('\n')
        FILE_REPORT.append('\n')

        # AP (APSCAN)
        print('{:<10} {} {:>10}'.format('='*10, 'NETWORK AP SCAN', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'NETWORK AP SCAN', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        apscan_cmd = "import network;network.WLAN(network.AP_IF).status('stations');gc.collect()"
        if apstat_info[:-2][0] != 'False':
            if info['sysname'] != 'esp8266':
                esp32.output = None
                while esp32.output is None:
                    esp32.cmd(apscan_cmd, silent=True)
                print('Found {} devices'.format(len(esp32.output)))
                FILE_REPORT.append(
                    'Found {} devices'.format(len(esp32.output)))
                mac_addr = []
                for dev in esp32.output:
                    bytdev = bytearray(dev[0])
                    mac_ad = ':'.join(str(val) for val in bytdev)
                    mac_addr.append(mac_ad)
                    print('MAC addres: {}'.format(mac_ad))
                    FILE_REPORT.append('MAC addres: {}'.format(mac_ad))
            else:
                print('ESP8266 NOT SUPPORTED')
                FILE_REPORT.append('ESP8266 NOT SUPPORTED')
        else:
            print('AP DISABLED')
            FILE_REPORT.append('AP DISABLED')
        # CAT 'BOOT.PY'
        print('\n')
        FILE_REPORT.append('\n')
        print('{:<10} {} {:>10}'.format('='*10, 'boot.py CONTENT', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'boot.py CONTENT', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.long_output = []
        while len(esp32.long_output) == 0:
            esp32.cmd('from upysh import *', silent=True, capture_output=True)
        esp32.long_output = []
        while len(esp32.long_output) == 0:
            esp32.cmd("cat('boot.py')", silent=True, capture_output=True)
        for val in esp32.long_output:
            print(val)
            FILE_REPORT.append(val)
        print('\n')
        FILE_REPORT.append('\n')
        # CAT 'MAIN.PY'

        print('{:<10} {} {:>10}'.format('='*10, 'main.py CONTENT', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'main.py CONTENT', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.long_output = []
        while len(esp32.long_output) == 0:
            esp32.cmd("cat('main.py');gc.collect()",
                      silent=True, capture_output=True)
        for val in esp32.long_output:
            print(val)
            FILE_REPORT.append(val)
        print('\n')
        FILE_REPORT.append('\n')
        # CAT 'ERROR.LOG'

        print('{:<10} {} {:>10}'.format('='*10, 'error.log CONTENT', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'error.log CONTENT', '='*10))
        print('\n')
        FILE_REPORT.append('\n')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd("import uos;uos.listdir('/')", silent=True)
        if 'error.log' in esp32.output:
            try:
                esp32.long_output = []
                while len(esp32.long_output) == 0:
                    esp32.cmd("cat('error.log');gc.collect()",
                              silent=True, capture_output=True)
                for val in esp32.long_output:
                    print(val)
                    FILE_REPORT.append(val)
            except Exception as e:
                pass
        else:
            print('error.log NOT FOUND')
            FILE_REPORT.append('error.log NOT FOUND')
        print('\n')
        FILE_REPORT.append('\n')
        if sd_ismounted:
            print('Looking for error.log in SD:')
            print('\n')
            FILE_REPORT.append('\n')
            esp32.output = None
            while esp32.output is None:
                esp32.cmd("import uos;uos.listdir('/sd')", silent=True)
            if 'error.log' in esp32.output:
                try:
                    esp32.long_output = []
                    while len(esp32.long_output) == 0:
                        esp32.cmd("cat('/sd/error.log');gc.collect()",
                                  silent=True, capture_output=True)
                    for val in esp32.long_output:
                        print(val)
                        FILE_REPORT.append(val)
                    print('\n')
                    FILE_REPORT.append('\n')
                except Exception as e:
                    pass
            else:
                print('error.log NOT FOUND')
                FILE_REPORT.append('error.log NOT FOUND')

        # HW
        # PINS STATE
        print('\n')
        FILE_REPORT.append('\n')
        print('{:<10} {} {:>10}'.format('='*10, 'PINS STATE', '='*10))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '='*10, 'PINS STATE', '='*10))
        pinlist = "[16, 17, 26, 25, 34, 39, 36, 4, 21, 13, 12, 27, 33, 15, 32, 14, 22, 23, 5, 18, 19]"
        if info['sysname'] == 'esp8266':
            pinlist = "[0,2,4,5,12,13,14,15]"
        machine_pin = "pins=[machine.Pin(i, machine.Pin.IN) for i in pin_list]"
        status = "dict(zip([str(p) for p in pins],[p.value() for p in pins]))"
        pin_status_cmd = "import machine;pin_list={};{};{};gc.collect()".format(
            pinlist, machine_pin, status)
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(pin_status_cmd, silent=True)
        pin_dict = esp32.output
        pin_list = list(pin_dict.keys())
        pin_list.sort()
        for key in pin_list:
            if pin_dict[key] == 1:
                print('{0:^10} | {1:^5} | HIGH'.format(key, pin_dict[key]))
                FILE_REPORT.append(
                    '{0:^10} | {1:^5} | HIGH'.format(key, pin_dict[key]))
            else:
                print('{0:^10} | {1:^5} |'.format(key, pin_dict[key]))
                FILE_REPORT.append(
                    '{0:^10} | {1:^5} |'.format(key, pin_dict[key]))
        print('\n')
        FILE_REPORT.append('\n')

        tdiff = time.time()-t0
        print('{:<10} {} {:>10}'.format(
            '*'*20, 'uPydev Diagnostics Test Finished!', '*'*20))
        FILE_REPORT.append('{:<10} {} {:>10}'.format(
            '*'*20, 'uPydev Diagnostics Test Finished!', '*'*20))
        print('TOTAL TIME: {} s'.format(round(tdiff, 2)))
        FILE_REPORT.append('TOTAL TIME: {} s'.format(round(tdiff, 2)))

        if save_rep:
            name_file = 'upyd_{}_{}_report.txt'.format(
                upy_id, datetime.now().strftime("%m-%d-%Y_at_%H-%M-%S"))
            if name_rep is not None:
                name_file = "{}.txt".format(name_rep)
            with open(name_file, 'w') as rfile:
                for line in FILE_REPORT:
                    if isinstance(line, str):
                        rfile.write(line)
                        rfile.write('\n')
                    else:
                        rfile.write("{}".format(line))
                        rfile.write('\n')
        if rst_opt:
            esp32.reset()

#############################################


def get_error_log(target, passwd, source=None):
    esp32 = W_UPYDEVICE(target, passwd)
    print('{:<10} {} {:>10}'.format('='*10, 'error.log CONTENT', '='*10))
    print('\n')
    if source is None:
        esp32.output = None
        while esp32.output is None:
            esp32.cmd(
                "from upysh import cat;import uos;uos.listdir('/')", silent=True)
        if 'error.log' in esp32.output:
            try:
                esp32.long_output = []
                while len(esp32.long_output) == 0:
                    esp32.cmd("cat('error.log');gc.collect()",
                              silent=True, capture_output=True)
                for val in esp32.long_output:
                    print(val)
            except Exception as e:
                pass
        else:
            print('error.log NOT FOUND')
    else:
        print('Looking for error.log in SD:')
        esp32.output = None
        while esp32.output is None:
            esp32.cmd("import uos;uos.listdir('/sd')", silent=True)
        if 'error.log' in esp32.output:
            try:
                esp32.long_output = []
                while len(esp32.long_output) == 0:
                    esp32.cmd("cat('/sd/error.log');gc.collect()",
                              silent=True, capture_output=True)
                for val in esp32.long_output:
                    print(val)
                print('\n')
            except Exception as e:
                pass
        else:
            print('error.log NOT FOUND')

#############################################

# STREAM TEST


def get_stream_test(ip, passwd, mode='download'):
    espdev = W_UPYDEVICE(ip, passwd)
    print('*'*10, ' upydev STREAM TEST ', '*'*10)
    espdev.output = None
    while espdev.output is None:
        espdev.cmd('from sync_tool import w_stream_writer; [1]', silent=True)
    data, total_time = stream_test(espdev, mode=mode)
    print('\n\nDone!')
    print('TEST RESULTS ARE:')
    print('TEST DURATION : {:.3f} (s)'.format(total_time))
    N_Bytes = len(data)
    print_sizefile('TEST DATA', N_Bytes)
    kBs = (N_Bytes/total_time)/1024
    print('DATA TRANSFER RATE (kBps): {:.3f} kB/s'.format(kBs))
    print('DATA TRANSFER RATE (Mbps): {:.3f} Mbps'.format((kBs*8)/1000))


#############################################

def sysctl(ip, passwd):
    espdev = W_UPYDEVICE(ip, passwd)
    sys_cmd = "import {}"
    if args.start is not None:
        print('Loading {} script...'.format(args.start))
        sys_cmd = sys_cmd.format(args.start)
        espdev.cmd_nb(sys_cmd, block_dev=False)
        print('Done!')
    elif args.stop is not None:
        reload = "del(sys.modules['{}'])".format(args.stop)
        print('Stopping {} script...'.format(args.stop))
        espdev.kbi()
        print('Unloading {} script...'.format(args.stop))
        reload_cmd = "import sys;{};gc.collect();print('Script unloaded!')".format(reload)
        time.sleep(1)
        espdev.cmd(reload_cmd)
        print('Done!')

#############################################

def log_script(ip, passwd, dev='', log=None):
    dir = ''
    script_filepy = args.f
    script_name = script_filepy.split('.')[0]
    reload = "del(sys.modules['{}'])".format(script_name)
    run_cmd = "import {};".format(script_name)
    e_keywords = ['Traceback', '<module>', 'Error', 'ERROR']
    # print(run_cmd)
    if args.s is not None:
        dir = args.s
        sd_path = "import sys;sys.path.append('/{}')".format(dir)
        run_cmd = "{};import {}".format(
            sd_path, script_name)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        run_cmd, ip, passwd)
    # print(run_cmd_str)
    run_live_cmd = shlex.split(run_cmd_str)
    log.info('Running {}...'.format(args.f))
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    if any([w in resp[4:] for w in e_keywords]):
                        log.error('{}{}'.format(dev, resp[4:]))
                    else:
                        log.info('{}{}'.format(dev, resp[4:]))
                else:
                    if any([w in resp for w in e_keywords]):
                        log.error('{}{}'.format(dev, resp))
                    else:
                        log.info('{}{}'.format(dev, resp))
            else:
                print(resp)
    except KeyboardInterrupt:
        try:
            log.info('...closing...')
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print(message[:-1].decode())
            for i in range(10):
                proc.stdout.readlines()

            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, ip, passwd)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
        except KeyboardInterrupt:
            log.info('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, ip, passwd)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
    # Reload sys.modules and sys.path
    time.sleep(1)
    reload_cmd = "import sys;{};gc.collect()".format(reload)
    if args.s is not None:
        reload_syspath = "sys.path.remove('/{}')".format(dir)
        reload_cmd = "{};{};gc.collect()".format(reload, reload_syspath)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        reload_cmd, ip, passwd)
    reload_cmd_resp = run_command_rl(cmd_str)
    log.info('Done!')


def get_log_script(ip, passwd):
    script_filepy = args.f
    script_name = script_filepy.split('.')[0]
    filelog_path = "{}/.upydev_logs/".format(os.environ['HOME'])
    filelog_daemon = ''.join([filelog_path, '{}_daemon.log'.format(script_name)])
    if not args.daemon:
        if not args.stopd:
            if '.upydev_logs' not in os.listdir("{}".format(os.environ['HOME'])):
                os.mkdir("{}/.upydev_logs".format(os.environ['HOME']))

            # Logging Setup
            # filelog_path = "{}/.upydev_logs/".format(os.environ['HOME'])
            # filelog_daemon = ''.join([filelog_path, '{}_daemon.log'.format(script_name)])
            log_levels = {'debug': logging.DEBUG, 'info': logging.INFO,
                          'warning': logging.WARNING, 'error': logging.ERROR,
                          'critical': logging.CRITICAL}
            handler = logging.StreamHandler(sys.stdout)
            handler.setLevel(log_levels[args.dslev])
            logging.basicConfig(
                level=log_levels['debug'],
                format="%(asctime)s [%(name)s] [%(process)d] [%(threadName)s] [%(levelname)s]  %(message)s",
                handlers=[handler])
            log = logging.getLogger('upydev_{}'.format(script_name))  # setup one logger per device
            # log.setLevel(log_levels[args.dslev]) # MASTER LOG LEVEL
            # Filehandler for error
            fh_err = logging.FileHandler(''.join([filelog_path, '{}_error.log'.format(script_name)]))
            fh_err.setLevel(log_levels[args.dflev])
            # Formatter for errors
            fmt_err = logging.Formatter("%(asctime)s [%(name)s] [%(process)d] [%(threadName)s] [%(levelname)s]  %(message)s")
            fh_err.setFormatter(fmt_err)
            log.addHandler(fh_err)

            log_script(ip=ip, passwd=passwd, log=log)
        else:
            print('Stopping daemon log...')
            print(stop_by_pid(proc_name='upydev log'))
            time.sleep(1)
            espdev = W_UPYDEVICE(ip, passwd)
            print('Stopping script...')
            print(stop_by_pid(proc_name='web_repl_cmd_r'))
            espdev.kbi(output=False)
            time.sleep(1)
            espdev.output = None
            while espdev.output is None:
                reload = "import sys;del(sys.modules['{}']);[1]".format(script_name)
                espdev.cmd(reload, silent=True)
            print('Done!')

    else:
        single_command = []
        for i in range(len(sys.argv[2:])):
            if sys.argv[2:][i] != '-daemon':
                single_command.append(sys.argv[2:][i])
        gcommand = ' '.join(single_command)
        daemon_cmd = "nohup upydev log {} > {} &".format(gcommand, filelog_daemon)
        subprocess.call(daemon_cmd, shell=True)  # is this still safe?
        print('Running upydev log daemon-like mode'.format(parser.version))
        print('Logging to {} with level: {}'.format('{}_daemon.log'.format(script_name), args.dslev))
        print("Do '$ upydev log -stopd -f {}' to stop the daemon".format(script_name))


# from https://stackoverflow.com/questions/12523044/how-can-i-tail-a-log-file-in-python
def follow_daemon_log():
    script_filepy = args.f
    script_name = script_filepy.split('.')[0]
    filelog_path = "{}/.upydev_logs/".format(os.environ['HOME'])
    filelog_daemon = ''.join([filelog_path, '{}_daemon.log'.format(script_name)])

    f = subprocess.Popen(['tail', '-F', filelog_daemon],
                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    while True:
        try:
            line = f.stdout.readline()
            print(line[:-1].decode())
        except KeyboardInterrupt:
            print("Unfollowing, do '$ upydev log -stopd -f {}' to stop the daemon".format(script_name))
            break


def get_cmd(raw):
    shcmd = shlex.split(raw)
    output = subprocess.check_output(shcmd).decode()
    return output


def stop_by_pid(raw='ps ax', proc_name='upydev log'):  # upydev log, web_repl_cmd_r
    shcmd = shlex.split(raw)
    output = subprocess.check_output(shcmd).decode()
    pline = [line for line in output.split('\n') if proc_name in line]
    if len(pline) > 0:
        pid = pline[0].strip().split(' ')[0]
        k = get_cmd('kill {}'.format(pid))
        return 'Daemon process {} with PID: {} stopped'.format(proc_name, pid)
    else:
        return('NO DAEMON PROCESS FOUND')


#############################################
def update_upyutils(ip, passwd):
    print('Updating device upyutils...')
    # sync_tool.py
    sync_tool = '{}/sync_tool.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # upylog.py
    upylog = '{}/upylog.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # sync_tool.py
    upynotify = '{}/upynotify.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # upysecrets.py
    upysecrets = '{}/upysecrets.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # upysh2.py
    upysh2 = '{}/upysh2.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # ssl_repl.py
    ssl_repl = '{}/ssl_repl.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # uping.py
    uping = '{}/uping.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # time_it.py
    time_it = '{}/time_it.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # wss_repl.py
    wss_repl = '{}/wss_repl.py'.format(upydev.__path__[0]+'/upyutils_dir')
    # wss_helper.py
    wss_helper = '{}/wss_helper.py'.format(upydev.__path__[0]+'/upyutils_dir')
    args.fre = [sync_tool, upylog, upynotify, upysecrets, upysh2,
                ssl_repl, uping, time_it, wss_repl, wss_helper]
    args.dir = 'lib'
    put_multiple_f(ip=ip, passwd=passwd)


#############################################
def debug_upyscript(ip, passwd, upyfile):
    print('Loading {} ...'.format(upyfile))
    print('ENTER: EXECUTE NEXT LINE, C: BREAK')
    dbug_wrepl_tool_cmd = 'dbg_wrepl -f{} -t {} -p {}'.format(upyfile, ip, passwd)
    dbg_cmd = shlex.split(dbug_wrepl_tool_cmd)
    try:
        subprocess.call(dbg_cmd)
    except KeyboardInterrupt:
        time.sleep(1)
        kbi(ip, passwd)

#############################################
# CRYPTO


def get_rsa_key(ip, passwd):
    print('Generating RSA key...')
    print('Getting unique id...')
    espdev = W_UPYDEVICE(ip, passwd)
    espdev.open_wconn()
    espdev.wr_cmd("from machine import unique_id; unique_id()",
                 silent=True, follow=False)
    unique_id = hexlify(espdev.output).decode()
    print('ID: {}'.format(unique_id))
    pkey = rsa_keygen(dir=upydev.__path__[0]+'/', show_key=args.show_key,
                      id=unique_id)
    print('Done!')
    espdev.close_wconn()
    if args.tfkey:
        print('Transfering RSA key to the device...')
        args.f = '{}/{}'.format(upydev.__path__[0], 'upy_pub_rsa{}.key'.format(unique_id))
        put_f(ip, passwd, rst=args.rst)
        print('Done!')


def refresh_wrkey(ip, passwd):
    if args.localkey_id is None:
        print('Generating new random WebREPL password...')
        print('Getting unique id...')
        espdev = W_UPYDEVICE(ip, passwd)
        espdev.open_wconn()
        espdev.wr_cmd("from machine import unique_id; unique_id()",
                      silent=True, follow=False)
        unique_id = hexlify(espdev.output).decode()
        print('ID: {}'.format(unique_id))
        pvkey = load_rsa_key(dir=upydev.__path__[0]+'/', show_key=args.show_key,
                             id=unique_id)
        print('Generating random password from RSA key...')
        new_p, new_token = upy_keygen(pvkey)
        espdev.wr_cmd("from upysecrets import load_key, upy_keygen")
        espdev.wr_cmd("rk = load_key()")
        espdev.wr_cmd("newp = upy_keygen(rk, {})".format(new_token))
        print('New random password generated!')
    else:
        print('Generating new random WebREPL password...')
        unique_id = args.localkey_id
        print('ID: {}'.format(unique_id))
        pvkey = load_rsa_key(dir=upydev.__path__[0]+'/', show_key=args.show_key,
                             id=unique_id)
        print('Generating random password from RSA key...')
        new_p, new_token = upy_keygen(pvkey)
        print('Use this token to generate new password in the device:\n', new_token)
    upydev_ip = args.t
    upydev_pass = new_p
    upy_conf = {'ip': upydev_ip, 'passwd': upydev_pass}
    file_conf = 'upydev_.config'
    if args.localkey_id is None:
        espdev.close_wconn()
        espdev.reset(output=False)
    if args.g:
        file_conf = '{}/upydev_.config'.format(upydev.__path__[0])
    with open(file_conf, 'w') as config_file:
        config_file.write(json.dumps(upy_conf))
    if args.g:
        print('upy device settings saved globally!')
    else:
        print('upy device settings saved in working directory!')


#############################################
def cryp_wrepl(ip, passwd, usr=None):
    if not args.rkey:
        if not args.nem:
            if usr is not None:
                crypweb_repl_cmd_str = 'crypweb_repl -t {} -p {} -r -dev {}'.format(ip, passwd, usr)
            else:
                crypweb_repl_cmd_str = 'crypweb_repl -t {} -p {} -r'.format(ip, passwd)
            crypweb_repl_cmd = shlex.split(crypweb_repl_cmd_str)
            clean_exit = False
            while not clean_exit:
                try:
                    crypweb_repl = subprocess.call(crypweb_repl_cmd)
                    clean_exit = True
                except KeyboardInterrupt:
                    print('KeyboardInterrupt!')
                    print('Reinitiating CryptoWebREPL...')
                    crypweb_repl_cmd = crypweb_repl_cmd + ' -ping'
                    time.sleep(7)
                except Exception as e:
                    print('Reinitiating CryptoWebREPL...')
                    time.sleep(5)
        else:
            if usr is not None:
                crypweb_repl_cmd_str = 'crypweb_repl -t {} -p {} -dev {} -nem'.format(ip, passwd, usr)
            else:
                crypweb_repl_cmd_str = 'crypweb_repl -t {} -p {} -nem'.format(ip, passwd)
            crypweb_repl_cmd = shlex.split(crypweb_repl_cmd_str)
            clean_exit = False
            while not clean_exit:
                try:
                    crypweb_repl = subprocess.call(crypweb_repl_cmd)
                    clean_exit = True
                except KeyboardInterrupt:
                    print('KeyboardInterrupt!')
                    print('Reinitiating CryptoWebREPL...')
                    crypweb_repl_cmd = crypweb_repl_cmd + ' -ping'
                    time.sleep(7)
                except Exception as e:
                    print('Reinitiating CryptoWebREPL...')
                    time.sleep(5)

    if args.rkey:
        if usr is not None:
            crypweb_repl_cmd_str = 'crypweb_repl -t {} -p {} -dev {}'.format(ip, passwd, usr)
        else:
            crypweb_repl_cmd_str = 'crypweb_repl -t {} -p {}'.format(ip, passwd)
        crypweb_repl_cmd = shlex.split(crypweb_repl_cmd_str)
        try:
            crypweb_repl = subprocess.call(crypweb_repl_cmd)
        except KeyboardInterrupt:
            pass
            print('')

        refresh_wrkey(ip, passwd)
        sys.exit()


#############################################

def get_ssl_keycert(ip, passwd):
    print('Generating SSL ECDSA key and certificates...')
    ssl_ECDSA_key_certgen(ip, passwd, dir=upydev.__path__[0]+'/')
#############################################


def ssl_wrepl(ip, passwd, usr=None, wss=False):
    if not args.rkey:
        if not args.nem:
            if usr is not None:
                if wss:
                    sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -r -dev {} -wss'.format(ip, passwd, usr)
                else:
                    sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -r -dev {}'.format(ip, passwd, usr)
            else:
                if wss:
                    sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -r -wss'.format(ip, passwd)
                else:
                    sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -r'.format(ip, passwd)
            sslweb_repl_cmd = shlex.split(sslweb_repl_cmd_str)

            old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

            def preexec_function(action=old_action):
                signal.signal(signal.SIGINT, action)

            try:
                sslweb_repl = subprocess.call(sslweb_repl_cmd, preexec_fn=preexec_function)
            except KeyboardInterrupt:
                pass

        else:
            if usr is not None:
                sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -dev {} -nem'.format(ip, passwd, usr)
            else:
                sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -nem'.format(ip, passwd)
            sslweb_repl_cmd = shlex.split(sslweb_repl_cmd_str)

            old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

            def preexec_function(action=old_action):
                signal.signal(signal.SIGINT, action)

            try:
                sslweb_repl = subprocess.call(sslweb_repl_cmd, preexec_fn=preexec_function)
            except KeyboardInterrupt:
                pass

    if args.rkey:
        if usr is not None:
            sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {} -dev {}'.format(ip, passwd, usr)
        else:
            sslweb_repl_cmd_str = 'sslweb_repl -t {} -p {}'.format(ip, passwd)
        sslweb_repl_cmd = shlex.split(sslweb_repl_cmd_str)
        try:
            sslweb_repl = subprocess.call(sslweb_repl_cmd)
        except KeyboardInterrupt:
            pass
            print('')

        refresh_wrkey(ip, passwd)
        sys.exit()

#############################################


def sh_srepl(baud, port, usr=None):
        if baud is None:
            baud = 115200
        if usr is not None:
            sh_srepl_cmd_str = 'sh_srepl -t {} -p {} -r -dev {}'.format(baud, port, usr)
        else:
            sh_srepl_cmd_str = 'sh_srepl -t {} -p {} -r'.format(baud, port)
        sh_srepl_cmd = shlex.split(sh_srepl_cmd_str)

        old_action = signal.signal(signal.SIGINT, signal.SIG_IGN)

        def preexec_function(action=old_action):
            signal.signal(signal.SIGINT, action)

        try:
            sh_srepl = subprocess.call(sh_srepl_cmd, preexec_fn=preexec_function)
        except KeyboardInterrupt:
            pass


#############################################

def address_entry_point(entry_point, group_file=''):
    if group_file == '':
        group_file = 'UPY_G'
    # print(group_file)
    if '{}.config'.format(group_file) not in os.listdir() or args.g:
        group_file = '{}/{}'.format(upydev.__path__[0], group_file)
    with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
        devices = json.loads(group.read())
        # print(devices)
    devs = devices.keys()
    # NAME ENTRY POINT
    if entry_point in devs:
        dev_ip = devices[entry_point][0]
        dev_pass = devices[entry_point][1]
        return (dev_ip, dev_pass)
    else:
        print('Device not configured in global group: {}'.format(group_file))
        sys.exit()
#############################################


# keyword live output commands:
kw_loc = ['ping', 'run', 'get', 'put', 'timeit', 'install',
          'see', 'sync', 'wrepl', 'srepl', 'find', 'diagnose', 'd_sync',
          'stream_test', 'log', 'debug', 'gen_rsakey', 'rf_wrkey', 'upy'
          'ssl', 'shr', 'wssl', 'wssrepl', 'jupyterc']  # implemented till sync in GP
# diagnose in G

# UPYDEVICE CLASS


class UPYDEVICE:
    def __init__(self, devname, ip, passwd, command, port=8005):
        self.name = devname
        self.ip = ip
        self.password = passwd
        self.command = command
        self.port = port
        self.dev_process = multiprocessing.Process(
            target=self.run_command_callback)
        self.dev_process_raw = multiprocessing.Process(
            target=self.run_command_raw_callback)
        self.dev_ping = multiprocessing.Process(target=self.ping_callback)
        self.dev_run_script = multiprocessing.Process(
            target=run_script, args=(self.ip, self.password, '{}: '.format(self.name), ))
        self.dev_timeit_script = multiprocessing.Process(
            target=timeit_script, args=(self.ip, self.password, '{}: '.format(self.name), ))
        self.dev_put = multiprocessing.Process(
            target=put_f, args=(self.ip, self.password, args.rst))
        self.dev_put_multiple = multiprocessing.Process(
            target=put_multiple_f, args=(self.ip, self.password))
        self.dev_install = multiprocessing.Process(target=install_f, args=(
            self.ip, self.password, '{}: '.format(self.name), ))
        self.dev_get = multiprocessing.Process(target=get_f, args=(
            self.ip, self.password, '/{}_{}'.format(self.name, args.f)))
        self.dev_get_multiple = multiprocessing.Process(
            target=get_multiple_f, args=(self.ip, self.password, 'device_id', self.name))
        self.dev_sync = multiprocessing.Process(target=sync, args=(
            self.ip, self.password, '{}_{}'.format(self.name, args.f), self.port))
        self.dev_sync_multiple = multiprocessing.Process(target=sync_multiple_f, args=(
            self.ip, self.password, 'device_id', self.name, self.port))

    def run_command_callback(self):
        group_cmd_str = 'upydev {} -t {} -p {}'.format(
            self.command, self.ip, self.password)
        # print(group_cmd_str)
        group_cmd = shlex.split(group_cmd_str)
        try:
            proc = subprocess.Popen(
                group_cmd, stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT)
            while proc.poll() is None:
                print('{}:{}'.format(
                    self.name, proc.stdout.readline()[:-1].decode()))
        except KeyboardInterrupt:
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print('{}:{}'.format(self.name, message[:-1].decode()))

    def run_command_raw_callback(self):
        group_cmd_str = 'web_repl_cmd_r -c {} -t {} -p {} -wt 0.8'.format(
            self.command, self.ip, self.password)
        # print(group_cmd_str)
        group_cmd = shlex.split(group_cmd_str)
        try:
            proc = subprocess.Popen(
                group_cmd, stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT)
            for i in range(6):
                proc.stdout.readline()
            while proc.poll() is None:
                resp = proc.stdout.readline()[:-1].decode()
                if len(resp) > 0:
                    if resp[0] == '>':
                        print('{}:{}'.format(self.name, resp[4:]))
                    else:
                        print('{}:{}'.format(self.name, resp))
                else:
                    print(resp)
        except KeyboardInterrupt:
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print('{}:{}'.format(self.name, message[:-1].decode()))

    def ping_callback(self):
        ping_cmd_str = 'ping {}'.format(self.ip)
        ping_cmd = shlex.split(ping_cmd_str)
        try:
            proc = subprocess.Popen(
                ping_cmd, stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT)
            while proc.poll() is None:
                print('{}:{}'.format(
                    self.name, proc.stdout.readline()[:-1].decode()))
        except KeyboardInterrupt:
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print('{}:{}'.format(self.name, message[:-1].decode()))

    def run_command(self):
        self.dev_process.start()

    def run_command_raw(self):
        self.dev_process_raw.start()

    def ping(self):
        self.dev_ping.start()

    def run_scrpt(self):
        self.dev_run_script.start()

    def timeit_scrpt(self):
        self.dev_timeit_script.start()

    def put_df(self):
        self.dev_put.start()

    def put_multiple_df(self):
        self.dev_put_multiple.start()

    def install_df(self):
        self.dev_install.start()

    def get_df(self):
        self.dev_get.start()

    def get_multiple_df(self):
        self.dev_get_multiple.start()

    def sync_df(self):
        self.dev_sync.start()

    def sync_multiple_df(self):
        self.dev_sync_multiple.start()


#############################################

# UPYDEV MODES:

# @ ENTRY
if "@" in args.m:
    args.m, entry_point = args.m.split('@')

# CONFIG:
if args.m == 'config':
    if args.t is None or args.p is None:
        if args.sec:
            print('Secure config mode:')
            args.t = input('IP of device: ')
            args.p = getpass.getpass(prompt='Password: ', stream=None)
        else:
            print('Password and Target ip required, see -p, -t or -sec')
            sys.exit()
    upydev_ip = args.t
    upydev_pass = args.p
    upy_conf = {'ip': upydev_ip, 'passwd': upydev_pass}
    file_conf = 'upydev_.config'
    if args.g:
        file_conf = '{}/upydev_.config'.format(upydev.__path__[0])
    with open(file_conf, 'w') as config_file:
        config_file.write(json.dumps(upy_conf))
    if args.g:
        print('upy device settings saved globally!')
    else:
        print('upy device settings saved in working directory!')
    sys.exit()

# UPYDEV LOOKS FOR UPYDEV_.CONFIG FILE
if args.t is None:
    try:
        file_conf = 'upydev_.config'
        if file_conf not in os.listdir() or args.g:
            file_conf = '{}/upydev_.config'.format(upydev.__path__[0])

        with open(file_conf, 'r') as config_file:
            upy_conf = json.loads(config_file.read())
        args.t = upy_conf['ip']
        args.p = upy_conf['passwd']
        # @ ENTRY POINT
        if args.b is not None:
            if "@" in args.b:
                gf, entryp = args.b.split('@')
                args.t, args.p = address_entry_point(entryp, gf)
        if vars(args)['@'] is not None:
                entryp = vars(args)['@']
                args.t, args.p = address_entry_point(entryp)
        if args.apmd:
            args.t = '192.168.4.1'
        if args.st:
            print('Target:{}'.format(args.t))
    except Exception as e:
        print('upydev_.config file not found, please provide target and \
        password or create config file with command "config" (see help)')
        sys.exit()

# UPYDEV GROUP COMMAND
if args.G is not None:
    try:
        group_file = args.G
        # print(group_file)
        if '{}.config'.format(group_file) not in os.listdir() or args.g:
            group_file = '{}/{}'.format(upydev.__path__[0], args.G)
        if args.m == 'see':
            see()
            sys.exit()
        if args.m == 'mg_group':
            if args.add is not None:
                group_dict = dict(zip(args.add[::3], [
                                  [args.add[i], args.add[i+1]] for i in range(1, len(args.add)-1, 3)]))
                with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
                    devices = json.loads(group.read())
                    devices.update(group_dict)
                with open('{}.config'.format(group_file), 'w', encoding='utf-8') as group:
                    group.write(json.dumps(devices))
                print('Upy devices group updated!')
                print('GROUP NAME: {}'.format(group_file.split('/')[-1]))
                print('# DEVICES ADDED: {}'.format(len(group_dict.keys())))
                for key in group_dict.keys():
                    if '.' in group_dict[key][0]:
                        print('DEVICE NAME: {}, IP: {}'.format(
                            key, group_dict[key][0]))
                    else:
                        print('DEVICE NAME: {}, SERIAL PORT: {}'.format(
                            key, group_dict[key][1]))
            elif args.rm is not None:
                group_dict_rm = args.rm
                removed_devs = {}
                with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
                    devices = json.loads(group.read())
                    for dev in group_dict_rm:
                        # del devices[dev]
                        removed_devs.update({dev: devices.pop(dev)})

                with open('{}.config'.format(group_file), 'w', encoding='utf-8') as group:
                    group.write(json.dumps(devices))
                print('Upy devices group updated!')
                print('GROUP NAME: {}'.format(group_file.split('/')[-1]))
                print('# DEVICES REMOVED: {}'.format(len(removed_devs.keys())))
                for key in removed_devs.keys():
                    print('DEVICE NAME: {}, IP: {}'.format(
                        key, removed_devs[key][0]))

            sys.exit()
        print('Sending command to group: {}'.format(group_file.split('/')[-1]))
        with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
            devices = json.loads(group.read())
            # print(devices)
        devs = devices.keys()
        if args.devs is not None:
            devs = args.devs
        for dev in devs:
            single_command = []
            print('Device Name: {}'.format(dev))
            dev_ip = devices[dev][0]
            dev_pass = devices[dev][1]
            print('Device IP: {}'.format(devices[dev][0]))
            print('Sending command {} ...'.format(args.m))
            # print(sys.argv[1:])
            for i in range(len(sys.argv[1:])):
                if sys.argv[1:][i] != '-G':
                    if i == 0 or sys.argv[1:][i-1] != '-G':
                        if sys.argv[1:][i] not in devs and sys.argv[1:][i] != '-devs':
                            single_command.append(sys.argv[1:][i])
            gcommand = ' '.join(single_command)
            # print(gcommand.split())
            # print(gcommand)
            group_cmd_str = 'upydev {} -t {} -p {}'.format(
                gcommand, dev_ip, dev_pass)
            if group_cmd_str.split()[1] not in keywords_mode:
                # print(group_cmd_str.split()[1])
                group_cmd_str = 'upydev "{}" -t {} -p {}'.format(
                    gcommand, dev_ip, dev_pass)
            # print(group_cmd_str)
            group_cmd = shlex.split(group_cmd_str)
            if group_cmd_str.split()[1] not in kw_loc:
                try:
                    proc = subprocess.Popen(
                        group_cmd, stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT)
                    while proc.poll() is None:
                        print(proc.stdout.readline()[:-1].decode())
                except KeyboardInterrupt:
                    time.sleep(1)
                    result = proc.stdout.readlines()
                    for message in result:
                        print(message[:-1].decode())
            else:
                if group_cmd_str.split()[1] == 'ping':
                    ping(dev_ip)
                elif group_cmd_str.split()[1] == 'run':
                    run_script(dev_ip, dev_pass)
                elif group_cmd_str.split()[1] == 'timeit':
                    timeit_script(dev_ip, dev_pass)
                elif group_cmd_str.split()[1] == 'put':
                    if args.fre is None:
                        put_f(dev_ip, dev_pass, rst=args.rst)
                    else:
                        put_multiple_f(dev_ip, dev_pass)
                elif group_cmd_str.split()[1] == 'get':
                    if args.fre is None:
                        get_f(dev_ip, dev_pass,
                              id_file='/{}_{}'.format(dev, args.f))
                    else:
                        get_multiple_f(dev_ip, dev_pass,
                                       id_file_dev='device_id', device=dev)
                elif group_cmd_str.split()[1] == 'sync':
                    if args.fre is None:
                        sync(dev_ip, dev_pass, id_file='{}_{}'.format(dev, args.f))
                    else:
                        sync_multiple_f(dev_ip, dev_pass,
                                        id_file_dev='device_id', device=dev)
                elif group_cmd_str.split()[1] == 'install':
                    install_f(dev_ip, dev_pass)

                elif group_cmd_str.split()[1] == 'wrepl':
                    wrepl(dev_ip, dev_pass)
                elif group_cmd_str.split()[1] == 'diagnose':
                    if args.rep:
                        args.rep = True
                    if args.rst is None:
                        args.rst = True
                    else:
                        args.rst = False
                    localdev = False
                    if args.apmd:
                        localdev = True
                    diagnose(dev_ip, dev_pass, save_rep=args.rep,
                             rst_opt=args.rst, name_rep=args.n,
                             vers=parser.version, local=localdev)

        sys.exit()
    except Exception as e:
        print(e)
        print('file not found, please create a group first')
        sys.exit()


# UPYDEV GROUP COMMAND THREADED MODE:
if args.GP is not None:
    try:
        group_file = args.GP
        # print(group_file)
        if '{}.config'.format(group_file) not in os.listdir() or args.g:
            group_file = '{}/{}'.format(upydev.__path__[0], args.GP)
        print('Sending command to group: {}'.format(group_file.split('/')[-1]))
        with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
            devices = json.loads(group.read())
            # print(devices)
        devs = devices.keys()
        PORTS = [8005+i for i in range(len(devs))]
        PORTS_DICT = dict(zip(devs, PORTS))
        if args.devs is not None:
            devs = args.devs
            PORTS = [8005+i for i in range(len(devs))]
            PORTS_DICT = dict(zip(devs, PORTS))
        for dev in devs:
            print('Device Name: {} ; IP: {}'.format(dev, devices[dev][0]))
        print('Sending command {} ...'.format(args.m))
        # print(sys.argv[1:])
        single_command = []
        for i in range(len(sys.argv[1:])):
            if sys.argv[1:][i] != '-GP':
                if i == 0 or sys.argv[1:][i-1] != '-GP':
                    if sys.argv[1:][i] not in devs and sys.argv[1:][i] != '-devs':
                        single_command.append(sys.argv[1:][i])
        gcommand = ' '.join(single_command)
        # print(gcommand.split())
        # group_cmd_str = 'upydev {} -t {} -p {}'.format(gcommand, dev_ip, dev_pass)
        devs_dict = {dev: UPYDEVICE(
            dev, devices[dev][0], devices[dev][1], gcommand, PORTS_DICT[dev]) for dev in devs}
        if gcommand.split()[0] not in keywords_mode:
            devs_dict = {dev: UPYDEVICE(
                dev, devices[dev][0], devices[dev][1], '"{};gc.collect()"'.format(gcommand)) for dev in devs}
            for dev in devs_dict.keys():
                devs_dict[dev].run_command_raw()
            try:
                while True:
                    dev_proc_state = [
                        devs_dict[dev].dev_process_raw.is_alive() for dev in devs_dict.keys()]
                    if all(state is False for state in dev_proc_state):
                        time.sleep(1)
                        print('Done!')
                        break
            except KeyboardInterrupt:
                time.sleep(2)
                while True:
                    dev_proc_state = [
                        devs_dict[dev].dev_process_raw.is_alive() for dev in devs_dict.keys()]
                    if all(state is False for state in dev_proc_state):
                        time.sleep(1)
                        for dev in devs_dict.keys():
                            devs_dict[dev].dev_process_raw.terminate()
                        break

        elif gcommand.split()[0] not in kw_loc:
            for dev in devs_dict.keys():
                devs_dict[dev].run_command()
            try:
                while True:
                    dev_proc_state = [
                        devs_dict[dev].dev_process.is_alive() for dev in devs_dict.keys()]
                    if all(state is False for state in dev_proc_state):
                        time.sleep(1)
                        print('Done!')
                        break
            except KeyboardInterrupt:
                time.sleep(2)
                while True:
                    dev_proc_state = [
                        devs_dict[dev].dev_process.is_alive() for dev in devs_dict.keys()]
                    if all(state is False for state in dev_proc_state):
                        time.sleep(1)
                        for dev in devs_dict.keys():
                            devs_dict[dev].dev_process.terminate()
                        break
        else:
            # print('Mode not implemented yet')
            if gcommand.split()[0] == 'ping':
                for dev in devs_dict.keys():
                    devs_dict[dev].ping()

                try:
                    while True:
                        time.sleep(0.1)
                except KeyboardInterrupt:
                    time.sleep(1)
                    for dev in devs_dict.keys():
                        devs_dict[dev].dev_ping.terminate()

            elif gcommand.split()[0] == 'put':
                if args.fre is None:
                    for dev in devs_dict.keys():
                        devs_dict[dev].put_df()
                else:
                    for dev in devs_dict.keys():
                        devs_dict[dev].put_multiple_df()

            elif gcommand.split()[0] == 'get':
                    if args.fre is None:
                        for dev in devs_dict.keys():
                            devs_dict[dev].get_df()
                    else:
                        for dev in devs_dict.keys():
                            devs_dict[dev].get_multiple_df()

            elif gcommand.split()[0] == 'sync':
                if args.fre is None:
                    for dev in devs_dict.keys():
                        devs_dict[dev].sync_df()
                else:
                    for dev in devs_dict.keys():
                        devs_dict[dev].sync_multiple_df()

            elif gcommand.split()[0] == 'install':
                for dev in devs_dict.keys():
                    devs_dict[dev].install_df()

            elif gcommand.split()[0] == 'timeit':
                for dev in devs_dict.keys():
                    devs_dict[dev].timeit_scrpt()

            elif gcommand.split()[0] == 'run':
                for dev in devs_dict.keys():
                    devs_dict[dev].run_scrpt()

                try:
                    while True:
                        dev_proc_state = [
                            devs_dict[dev].dev_run_script.is_alive() for dev in devs_dict.keys()]
                        if all(state is False for state in dev_proc_state):
                            time.sleep(0.1)
                            break
                except KeyboardInterrupt:
                    time.sleep(2)
                    while True:
                        dev_proc_state = [
                            devs_dict[dev].dev_run_script.is_alive() for dev in devs_dict.keys()]
                        if all(state is False for state in dev_proc_state):
                            time.sleep(1)
                            for dev in devs_dict.keys():
                                devs_dict[dev].dev_run_script.terminate()
                            break

        sys.exit()
    except Exception as e:
        print(e)
        print('file not found, please create a group first')
        sys.exit()

# UPYDEV RAW COMMAND MODE: (WHEN ARGUMENT Mode is not in keyword list)
if args.apmd:
    args.t = '192.168.4.1'
if args.m not in keywords_mode:
    cmd = args.m
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        cmd, args.t, args.p)
    cmd = shlex.split(cmd_str)
    try:
        proc = subprocess.Popen(
            cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()
            if b'\n' in resp:
                resp = resp[:-1].decode()
            else:
                resp = resp.decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except KeyboardInterrupt:
        time.sleep(1)
        result = proc.stdout.readlines()
        for message in result:
            print(message[:-1].decode())

    sys.exit()

# PUT:

elif args.m == 'put':
    if args.fre is None:
        put_f(args.t, args.p, rst=args.rst)
    else:
        put_multiple_f(args.t, args.p)
    sys.exit()

# GET :

elif args.m == 'get':
    if args.fre is None:
        get_f(args.t, args.p)
    else:
        get_multiple_f(args.t, args.p)
    sys.exit()

# SYNC :

elif args.m == 'sync':
    if args.fre is None:
        sync(args.t, args.p)
    else:
        sync_multiple_f(args.t, args.p)
    sys.exit()

# D_SYNC:
elif args.m == 'd_sync':
    dir_to_sync = args.dir
    args.dir = None
    esp_dev = W_UPYDEVICE(args.t, args.p)
    esp_dev.cmd('import uos')
    uos = UOS(esp_dev)
    d_sync_recursive(dir_to_sync, uos, root_sync_folder=dir_to_sync,
                     show_tree=args.tree)
    sys.exit()

# CMD (FOR DEBBUGING PURPOSE) # DEBUG:

elif args.m == 'cmd':
    if args.c is not None:
        cmd_str = 'upycmd -c "{}" -t {} -p {}'.format(
            args.c, args.t, args.p)
        command = shlex.split(cmd_str)
        process = subprocess.Popen(command, stdout=subprocess.PIPE)
        time.sleep(0.1)
        stdout = process.communicate()
        sys.exit()
    if args.r is not None:
        cmd_str = 'upycmd_r -c "{}" -t {} -p {}'.format(args.r, args.t, args.p)
        command = shlex.split(cmd_str)
        process = subprocess.Popen(command, stdout=subprocess.PIPE)
        time.sleep(0.1)
        stdout = process.communicate()
        try:
            resp = ast.literal_eval(
                stdout[0].decode('utf-8').split('\n')[0][4:-1])
        except Exception as e:
            try:
                resp = stdout[0].decode('utf-8').split('\n')[0]
            except Exception as e:
                resp = None

            pass
        print(resp)
        sys.exit()
    if args.rl is not None:
        web_repl_cmd = 'web_repl_cmd_r -p {} -t {} -c "{}"'.format(
            args.p, args.t, args.rl)
        cmd_resp = run_command_rl(web_repl_cmd)
        resp = cmd_resp[1]
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        print(line[4:])
                    else:
                        print(line)
                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        sys.exit()

# WEB REPL :

elif args.m == 'wrepl':
    wrepl(args.t, args.p)

# WEB SECURE REPL :

elif args.m == 'wssrepl':
    wssrepl(args.t, args.p)

# SERIAL REPL:

elif args.m == 'srepl':
    srepl(args.port)

# PING :

elif args.m == 'ping':
    ping(args.t)

# RUN :


elif args.m == 'run':
    run_script(args.t, args.p)
    sys.exit()

# TIMEIT:

elif args.m == 'timeit':
    timeit_script(args.t, args.p)
    sys.exit()

# INSTALL:

elif args.m == 'install':
    install_f(args.t, args.p)
    sys.exit()

# MPYX

elif args.m == 'mpyx':
    mpy_cmd_str = 'python -m mpy_cross {}'.format(args.f)
    mpy_cmd = shlex.split(mpy_cmd_str)
    try:
        proc = subprocess.Popen(
            mpy_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        while proc.poll() is None:
            print(proc.stdout.readline()[:-1].decode())
    except KeyboardInterrupt:
        time.sleep(1)
        result = proc.stdout.readlines()
        for message in result:
            print(message[:-1].decode())

# FW
elif args.m == 'fw':
    if args.md[0] == 'list':
        if len(args.md) > 1:
            if args.md[1] == 'serial_ports':
                ls_cmd_str = "/dev/tty.*"
                print('Available Serial ports are:')
                for port in glob.glob(ls_cmd_str):
                    print(port)
            if args.md[1] == 'latest':
                today = datetime.strftime(datetime.now(), "%Y%m%d")
                print('Latest Firmware versions found for {}:'.format(args.b))
                fw_v = get_fw_versions(args.b)
                fw_v_latest = [v for v in fw_v[1] if today in v]
                days_before = 0
                while len(fw_v_latest) == 0:
                    if len(fw_v[1]) == 0:
                        break
                    days_before += 1
                    latest_fw = datetime.strftime(datetime.now()-timedelta(days=days_before), "%Y%m%d")
                    fw_v_latest = [v for v in fw_v[1] if latest_fw in v]
                if args.n is not None:
                    if args.n != 'def':
                        fw_v_latest_opt = [v for v in fw_v_latest if args.n in v]
                        for v in fw_v_latest_opt:
                            print('- {}'.format(v))
                    else:
                        print('- {}'.format(fw_v_latest[0]))
                else:
                    for v in fw_v_latest:
                        print('- {}'.format(v))
                # fw_v_latest_link = fw_v[0][fw_v_latest]

        else:
            fw_v = get_fw_versions(args.b)[1]
            if args.n is not None:
                print('Firmware versions found for {}-{}:'.format(args.b, args.n))
                if args.n != 'def':
                    fw_v_opt = [v for v in fw_v if args.n in v]
                    for v in fw_v_opt:
                        print('- {}'.format(v))
                else:
                    print('- {}'.format(fw_v[0]))
            else:
                print('Firmware versions found for {}:'.format(args.b))
                for version in fw_v:
                    print('- {}'.format(version))
    elif args.md[0] == 'get':
        if args.md[1] == 'latest':
            today = datetime.strftime(datetime.now(), "%Y%m%d")
            print('Latest version found for {}:'.format(args.b))
            fw_v = get_fw_versions(args.b)
            fw_v_latest = [v for v in fw_v[1] if today in v]
            days_before = 0
            while len(fw_v_latest) == 0:
                if len(fw_v[1]) == 0:
                    break
                days_before += 1
                latest_fw = datetime.strftime(datetime.now()-timedelta(days=days_before), "%Y%m%d")
                fw_v_latest = [v for v in fw_v[1] if latest_fw in v]
            if args.n is not None:
                if args.n != 'def':
                    fw_v_latest = [v for v in fw_v_latest if args.n in v]
                    for v in fw_v_latest:
                        print('- {}'.format(v))
            else:
                print('- {}'.format(fw_v_latest[0]))
            if len(fw_v_latest) >= 1:
                fw_v_latest = fw_v_latest[0]
                print('Firmware selected: {}'.format(fw_v_latest))
                fw_v_latest_link = fw_v[0][fw_v_latest]
                print('Downloading {} ...'.format(fw_v_latest))
                curl_cmd_str = "curl -O '{}'".format(fw_v_latest_link)
                curl_cmd = shlex.split(curl_cmd_str)
                try:
                    proc = subprocess.call(curl_cmd)
                    print('Done!')
                except KeyboardInterrupt:
                    print('Operation cancelled')
            else:
                print('No firmware available that match {}'.format(args.n))
        else:
            fw_v_link = get_fw_versions(args.md[1])[0][args.md[1]]
            print('Downloading {} ...'.format(args.md[1]))
            curl_cmd_str = "curl -O '{}'".format(fw_v_link)
            curl_cmd = shlex.split(curl_cmd_str)
            try:
                proc = subprocess.call(curl_cmd)
                print('Done!')
            except KeyboardInterrupt:
                print('Operation cancelled')

elif args.m == 'flash':
    if 'esp32' in args.f:
        print('Flashing firmware {} ...'.format(args.f))
        esptool_cmd_str = "esptool.py --chip esp32 --port {} write_flash -z 0x1000 {}".format(
            args.port, args.f)
        esptool_cmd = shlex.split(esptool_cmd_str)
        try:
            proc = subprocess.call(esptool_cmd)
            print('Done!')
        except KeyboardInterrupt:
            print('Operation cancelled')
    elif 'esp8266' in args.f:
        esptool_cmd_str = "esptool.py --port {} --baud 460800 write_flash --flash_size=detect 0 {}".format(
            args.port, args.f)
        esptool_cmd = shlex.split(esptool_cmd_str)
        try:
            proc = subprocess.call(esptool_cmd)
            print('Done!')
        except KeyboardInterrupt:
            print('Operation cancelled')
    sys.exit()

# SEE
elif args.m == 'see':
    see()
    sys.exit()

# FIND

elif args.m == 'find':
    find_devices()
    sys.exit()

# DIAGNOSE
elif args.m == 'diagnose':
    if args.rst is None:
        args.rst = True
    else:
        args.rst = False
    localdev = False
    if args.apmd:
        localdev = True
    diagnose(args.t, args.p, save_rep=args.rep, rst_opt=args.rst,
             name_rep=args.n, vers=parser.version, local=localdev)
    sys.exit()

# ERRLOG
elif args.m == 'errlog':
    get_error_log(args.t, args.p, args.s)
    sys.exit()

# STREAM_TEST

elif args.m == 'stream_test':
    get_stream_test(args.t, args.p)
    sys.exit()

# SYSCTL

elif args.m == 'sysctl':
    sysctl(args.t, args.p)
    sys.exit()

# LOG

elif args.m == 'log':
    if not args.follow:
        get_log_script(args.t, args.p)
    else:
        follow_daemon_log()
    sys.exit()

# UPDATE UPYUTILS
elif args.m == 'update_upyutils':
    update_upyutils(args.t, args.p)
    sys.exit()

# _DEBUG SCRIPT
elif args.m == 'debug':
    debug_upyscript(args.t, args.p, args.f)
    sys.exit()

# GEN_RSAKEY

elif args.m == 'gen_rsakey':
    get_rsa_key(args.t, args.p)
    sys.exit()

# RF_WRKEY
elif args.m == 'rf_wrkey':
    refresh_wrkey(args.t, args.p)
    sys.exit()

# CRYP_WREPL
elif args.m == 'crypto_wrepl':
    cryp_wrepl(args.t, args.p)

# SSLGEN_KEY

elif args.m == 'sslgen_key':
    get_ssl_keycert(args.t, args.p)
    sys.exit()

# SSL_WREPL
elif args.m == 'ssl_wrepl':
    if vars(args)['@'] is not None:
        entryp = vars(args)['@']
        ssl_wrepl(args.t, args.p, usr=entryp)
    else:
        ssl_wrepl(args.t, args.p)

elif args.m == 'sh_srepl':
    if vars(args)['@'] is not None:
        entryp = vars(args)['@']
        sh_srepl(args.t, args.p, usr=entryp)
    else:
        if args.port is not None:
            sh_srepl(args.b, args.port)
        else:
            sh_srepl(args.t, args.p)

# MAKE AN DIRECT ENTRY WITH @ FORMAT

elif args.m == 'upy':
    group_file = 'UPY_G'
    # print(group_file)
    if '{}.config'.format(group_file) not in os.listdir() or args.g:
        group_file = '{}/{}'.format(upydev.__path__[0], group_file)
    with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
        devices = json.loads(group.read())
        # print(devices)
    devs = devices.keys()
    # NAME ENTRY POINT
    if entry_point in devs:
        dev_ip = devices[entry_point][0]
        if args.apmd:
            dev_ip = '192.168.4.1'
        dev_pass = devices[entry_point][1]
        try:
            cryp_wrepl(dev_ip, dev_pass, usr=entry_point)
        except Exception as e:
            print('PASSWORD INVALID or DEVICE NOT REACHABLE')
    else:
        # get dev by ip
        which_dev = [devices[dev][1] for dev in devs if devices[dev][0] == entry_point ]
        if len(which_dev) > 0:
            dev_ip = entry_point
            dev_pass = which_dev[0]
            try:
                cryp_wrepl(dev_ip, dev_pass, usr=entry_point)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')
        else:
            print('Device Not configured PASSWORD NEEDED')
            my_p = getpass.getpass(prompt='Enter passphrase for device {} :'.format(entry_point), stream=None)
            try:
                cryp_wrepl(entry_point, my_p, usr=entry_point)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')

# MAKE AN DIRECT ENTRY WITH @ FORMAT

elif args.m == 'ssl':
    group_file = 'UPY_G'
    # print(group_file)
    if '{}.config'.format(group_file) not in os.listdir() or args.g:
        group_file = '{}/{}'.format(upydev.__path__[0], group_file)
    with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
        devices = json.loads(group.read())
        # print(devices)
    devs = devices.keys()
    # NAME ENTRY POINT
    if entry_point in devs:
        dev_ip = devices[entry_point][0]
        if args.apmd:
            dev_ip = '192.168.4.1'
        dev_pass = devices[entry_point][1]
        try:
            ssl_wrepl(dev_ip, dev_pass, usr=entry_point)
        except Exception as e:
            print('PASSWORD INVALID or DEVICE NOT REACHABLE')
    else:
        # get dev by ip
        which_dev = [devices[dev][1] for dev in devs if devices[dev][0] == entry_point ]
        if len(which_dev) > 0:
            dev_ip = entry_point
            dev_pass = which_dev[0]
            try:
                ssl_wrepl(dev_ip, dev_pass, usr=entry_point)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')
        else:
            print('Device Not configured PASSWORD NEEDED')
            my_p = getpass.getpass(prompt='Enter passphrase for device {} :'.format(entry_point), stream=None)
            try:
                ssl_wrepl(entry_point, my_p, usr=entry_point)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')

elif args.m == 'wssl':
    group_file = 'UPY_G'
    # print(group_file)
    if '{}.config'.format(group_file) not in os.listdir() or args.g:
        group_file = '{}/{}'.format(upydev.__path__[0], group_file)
    with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
        devices = json.loads(group.read())
        # print(devices)
    devs = devices.keys()
    # NAME ENTRY POINT
    if entry_point in devs:
        dev_ip = devices[entry_point][0]
        if args.apmd:
            dev_ip = '192.168.4.1'
        dev_pass = devices[entry_point][1]
        try:
            ssl_wrepl(dev_ip, dev_pass, usr=entry_point, wss=True)
        except Exception as e:
            print('PASSWORD INVALID or DEVICE NOT REACHABLE')
    else:
        # get dev by ip
        which_dev = [devices[dev][1] for dev in devs if devices[dev][0] == entry_point ]
        if len(which_dev) > 0:
            dev_ip = entry_point
            dev_pass = which_dev[0]
            try:
                ssl_wrepl(dev_ip, dev_pass, usr=entry_point, wss=True)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')
        else:
            print('Device Not configured PASSWORD NEEDED')
            my_p = getpass.getpass(prompt='Enter passphrase for device {} :'.format(entry_point), stream=None)
            try:
                ssl_wrepl(entry_point, my_p, usr=entry_point, wss=True)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')

elif args.m == 'set_wss':
    if not args.wss:
        print('Enabling WebSecureREPL...')
        wss_repl_cmd = 'import wss_repl;wss_repl.stop();wss_repl.start(ssl=True);wss_repl.set_ssl(True)\r'
        if '.' in args.t:
            wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True, ssl=args.wss, auth=args.wss)
            bytes_sent = wsdevice.write(wss_repl_cmd)
            wsdevice.close_wconn()
            print('\rWebSecureREPL enabled!')
        else:
            pass
    else:
        print('Switching to WebREPL...')
        wrepl_cmd = 'import wss_repl;wss_repl.stop();wss_repl.start(ssl=False);wss_repl.set_ssl(False)\r'
        if '.' in args.t:
            wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True, ssl=args.wss, auth=args.wss)
            bytes_sent = wsdevice.write(wrepl_cmd)
            wsdevice.close_wconn()
            print('\rWebREPL enabled!, WebSecureREPL disabled!')
        else:
            pass

elif args.m == 'shr':
    group_file = 'UPY_G'
    # print(group_file)
    if '{}.config'.format(group_file) not in os.listdir() or args.g:
        group_file = '{}/{}'.format(upydev.__path__[0], group_file)
    with open('{}.config'.format(group_file), 'r', encoding='utf-8') as group:
        devices = json.loads(group.read())
        # print(devices)
    devs = devices.keys()
    # NAME ENTRY POINT
    if entry_point in devs:
        dev_baud = devices[entry_point][0]
        dev_port = devices[entry_point][1]
        try:
            sh_srepl(dev_baud, dev_port, usr=entry_point)
        except Exception as e:
            print('PASSWORD INVALID or DEVICE NOT REACHABLE')
    else:
        pass
        # get dev by ip
        which_dev = [devices[dev][1] for dev in devs if devices[dev][0] == entry_point ]
        if len(which_dev) > 0:
            dev_port = entry_point
            dev_baud = which_dev[0]
            try:
                sh_srepl(dev_baud, dev_port, usr=entry_point)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')
        else:
            try:
                if args.port is not None:
                    entry_point = args.port
                sh_srepl(115200, entry_point, usr=entry_point)
            except Exception as e:
                print('PASSWORD INVALID or DEVICE NOT REACHABLE')

# JUPYTER CONSOLE
elif args.m == 'jupyterc':
    jupyterc()

# MAKE_GROUP
elif args.m == 'make_group':
    group_file = '{}.config'.format(args.f)
    group_dict = dict(zip(args.devs[::3], [
                      [args.devs[i], args.devs[i+1]] for i in range(1, len(args.devs)-1, 3)]))
    if args.g:
        group_file = '{}/{}.config'.format(upydev.__path__[0], args.f)
    with open(group_file, 'w') as group:
        group.write(json.dumps(group_dict))

    if args.g:
        print('Group settings saved globally!')
    else:
        print('Group settings saved in working directory!')
    print('Upy devices group created!')
    print('GROUP NAME: {}'.format(args.f))
    print('# DEVICES: {}'.format(len(group_dict.keys())))
    for key in group_dict.keys():
        print('DEVICE NAME: {}, IP: {}'.format(key, group_dict[key][0]))
    sys.exit()

# * GENERAL COMMANDS *
# INFO :

elif args.m == 'info':
    info_cmd = 'import uos;uos.uname();gc.collect()'
    if '.' in args.t:
        wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True, ssl=args.wss, auth=args.wss)
        upyinfo = wsdevice.wr_cmd(info_cmd,
                                  silent=True, rtn_resp=True)
        wsdevice.close_wconn()
    else:
        sdevice = BASE_SERIAL_DEVICE(args.p, args.t)
        upyinfo = sdevice.cmd(info_cmd)
    try:
        dev_info = upyinfo.split("'")
        print('SYSTEM NAME: {}'.format(dev_info[1]))
        print('NODE NAME: {}'.format(dev_info[3]))
        print('RELEASE: {}'.format(dev_info[5]))
        print('VERSION: {}'.format(dev_info[7]))
        print('MACHINE: {}'.format(dev_info[9]))
    except Exception as e:
        pass
    sys.exit()

# ID:

elif args.m == 'id':
    uid_cmd = "import gc;from machine import unique_id;from ubinascii import hexlify;hexlify(unique_id());gc.collect()"
    if '.' in args.t:
        wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
        uid = wsdevice.wr_cmd(uid_cmd, silent=True, rtn_resp=True)
        wsdevice.close_wconn()
    else:
        sdevice = BASE_SERIAL_DEVICE(args.p, args.t)
        uid = sdevice.cmd(uid_cmd)
    print('ID: {}'.format(uid.decode()))
    sys.exit()

# UPYSH

elif args.m == 'upysh':
    upysh_cmd = "from upysh import *;gc.collect()"
    if '.' in args.t:
        wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
        wsdevice.wr_cmd(upysh_cmd, long_string=True)
        wsdevice.close_wconn()
    else:
        sdevice = BASE_SERIAL_DEVICE(args.p, args.t)
        resp = sdevice.cmd(upysh_cmd)
        print(resp)
    sys.exit()

# RESET

elif args.m == 'reset':
    reset(args.t, args.p)
    sys.exit()

# KEYBOARD Interrupt

elif args.m == 'kbi':
    kbi(args.t, args.p)
    sys.exit()

# UHELP

elif args.m == 'uhelp':
    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
    help_cmd = "help();gc.collect()"
    wsdevice.wr_cmd(help_cmd, long_string=True)
    wsdevice.close_wconn()
    sys.exit()

# UMODULES

elif args.m == 'umodules':
    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
    help_cmd = "help('modules');gc.collect()"
    wsdevice.wr_cmd(help_cmd, long_string=True)
    wsdevice.close_wconn()
    sys.exit()

# MEM_INFO

elif args.m == 'mem_info':
    mem_cmd = "from micropython import mem_info;mem_info();gc.collect()"
    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
    RAM = wsdevice.wr_cmd(mem_cmd, silent=True, long_string=True, rtn_resp=True)
    wsdevice.close_wconn()
    mem_info = RAM.splitlines()[1]
    mem = {elem.strip().split(':')[0]: int(elem.strip().split(':')[
                      1]) for elem in mem_info[4:].split(',')}
    print("{0:12}{1:^12}{2:^12}{3:^12}{4:^12}".format(*['Memmory',
                                                        'Size', 'Used',
                                                        'Avail',
                                                        'Use%']))
    total_mem = mem['total']/1024
    used_mem = mem['used']/1024
    free_mem = mem['free']/1024
    total_mem_s = "{:.3f} KB".format(total_mem)
    used_mem_s = "{:.3f} KB".format(used_mem)
    free_mem_s = "{:.3f} KB".format(free_mem)

    print('{0:12}{1:^12}{2:^12}{3:^12}{4:>8}'.format('RAM', total_mem_s,
                                                      used_mem_s, free_mem_s,
                                                      "{:.1f} %".format((used_mem/total_mem)*100)))
    sys.exit()

# FILESIZE

elif args.m == 'filesize':
    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
    file_name = args.f
    if args.s is not None:
        file_name = '/{}/{}'.format(args.s, args.f)
    cmd_str = "import uos;uos.stat('{}');gc.collect()".format(file_name)
    if args.f is None:
        dir = ''
        if args.s is not None:
            dir = '/{}/'.format(args.s)
        cmd_str = "[(filename,uos.stat('{0}'+str(filename))[6]) for filename in uos.listdir('{0}')]".format(dir)
    filesize_info = wsdevice.wr_cmd(cmd_str, silent=True, rtn_resp=True)
    if len(filesize_info) > 0:
        if args.f is not None:
            size = filesize_info[6]
            print_sizefile(args.f, size)
        else:
            print_sizefile_all(filesize_info)
    wsdevice.close_wconn()
    sys.exit()

# FILESYS_INFO

elif args.m == 'filesys_info':
    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
    filesys = ''
    filesys_check = True
    if args.s is not None:
        filesys = '/{}'.format(args.s)
        filesys_check = wsdevice.wr_cmd("import uos;'{}' in uos.listdir();gc.collect()".format(filesys.split('/')[1]), silent=True, rtn_resp=True)
    cmd_str = "import uos;uos.statvfs('{}');gc.collect()".format(filesys)
    if filesys == '':
        filesys = 'Flash'
    if filesys_check:
        resp = wsdevice.wr_cmd(cmd_str, silent=True, rtn_resp=True)
        size_info = resp
        total_b = size_info[0] * size_info[2]
        used_b = (size_info[0] * size_info[2]) - (size_info[0] * size_info[3])
        total_mem = print_filesys_info(total_b)
        free_mem = print_filesys_info(size_info[0] * size_info[3])
        used_mem = print_filesys_info(used_b)
        if filesys == 'Flash':
            mounted_on = '/'
        else:
            mounted_on = filesys
        print("{0:12}{1:^12}{2:^12}{3:^12}{4:^12}{5:^12}".format(*['Filesystem',
                                                                   'Size', 'Used',
                                                                   'Avail',
                                                                   'Use%', 'Mounted on']))
        print('{0:12}{1:^12}{2:^12}{3:^12}{4:>8}{5:>5}{6:12}'.format(filesys, total_mem,
                                                                used_mem, free_mem,
                                                                "{:.1f} %".format((used_b/total_b)*100), ' ', mounted_on))
    else:
        print('{} not mounted'.format(filesys))
    wsdevice.close_wconn()
    sys.exit()

# NETINFO :

elif args.m == 'netinfo':
    net_cmd = "import network;network.WLAN(network.STA_IF).ifconfig();"
    net_info_cmd = "network.WLAN(network.STA_IF).config('essid');"
    net_signal_cmd = "network.WLAN(network.STA_IF).status('rssi');gc.collect()"
    net_comp_cmd = '{}{}{}'.format(net_cmd, net_info_cmd, net_signal_cmd)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        net_comp_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    net_info_list.append(line[4:])
                else:
                    net_info_list.append(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(net_info_list) > 0:
        print('IF CONFIG: {}'.format(net_info_list[0]))
        print('ESSID: {}'.format(net_info_list[1]))
        print('RSSI: {} dB'.format(net_info_list[2]))
    sys.exit()

# NETINFOT

elif args.m == 'netinfot':
    net_cmd = "import network;network.WLAN(network.STA_IF).ifconfig();"
    net_info_cmd = "network.WLAN(network.STA_IF).config('essid');"
    net_signal_cmd = "network.WLAN(network.STA_IF).status('rssi');gc.collect()"
    net_comp_cmd = '{}{}{}'.format(net_cmd, net_info_cmd, net_signal_cmd)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        net_comp_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    net_info_list.append(line[4:])
                else:
                    net_info_list.append(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('IF CONFIG:')
    print('=' * 106)
    print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
        'IP', 'SUBNET', 'GATEAWAY', 'DNS', 'ESSID', 'RSSI (dB)'))
    try:
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^15} | {5:^15} '.format(
            *ast.literal_eval(net_info_list[0]), net_info_list[1],
            net_info_list[2]))
    except Exception as e:
        print(e)
        pass

    sys.exit()

# NETSCAN

elif args.m == 'netscan':
    net_cmd = "import network;network.WLAN(network.STA_IF).scan();gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        net_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_scan_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    if line[4:][0] != '[':
                        print(line[4:])
                    else:
                        net_scan_list.append(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('=' * 110)
    print('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
        'ESSID', 'BSSID', 'CHANNEL', 'RSSI (dB)', 'AUTHMODE', 'HIDDEN'))
    print('=' * 110)
    for net in net_scan_list:
        netlist = ast.literal_eval(net)
        for netscan in netlist:
            auth = AUTHMODE_DICT[netscan[4]]
            vals = hexlify(netscan[1]).decode()
            bssid = ':'.join([vals[i:i+2] for i in range(0, len(vals), 2)])
            print('{0:^20} | {1:^25} | {2:^10} | {3:^15} | {4:^15} | {5:^10} '.format(
                netscan[0].decode(), bssid, netscan[2], netscan[3],
                auth, str(netscan[5])))
    sys.exit()

# NETSTAT_ON

elif args.m == 'netstat_on':
    help_cmd = "import network;network.WLAN(network.STA_IF).active(True);gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        help_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# NETSTAT_OFF

elif args.m == 'netstat_off':
    help_cmd = "import network;network.WLAN(network.STA_IF).active(False);gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        help_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# NETSTAT_CONN

elif args.m == 'netstat_conn':
    essid, passwd = args.wp
    connect_to = "network.WLAN(network.STA_IF).connect('{}', '{}')".format(
        essid, passwd)
    conn_cmd = "import network;{};gc.collect()".format(connect_to)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        conn_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# NETSTAT

elif args.m == 'netstat':
    help_cmd = "import network;network.WLAN(network.STA_IF).active();gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        help_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    netstat_info = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    netstat_info.append(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('STA ENABLED: {}'.format(netstat_info[0]))
    sys.exit()

# AP_ON

elif args.m == 'ap_on':
    help_cmd = "import network;network.WLAN(network.AP_IF).active(True);gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        help_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# AP_OFF

elif args.m == 'ap_off':
    help_cmd = "import network;network.WLAN(network.AP_IF).active(False);gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        help_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# APSTAT

elif args.m == 'apstat':
    ap_state = "network.WLAN(network.AP_IF).active()"
    ap_essid = "network.WLAN(network.AP_IF).config('essid')"
    ap_channel = "network.WLAN(network.AP_IF).config('channel')"
    ap_authmode = "network.WLAN(network.AP_IF).config('authmode')"
    ap_ifconfig = "network.WLAN(network.AP_IF).ifconfig()"
    apstat_cmd = "import network;{};{};{};{};{};gc.collect()".format(
        ap_state, ap_essid, ap_channel, ap_authmode, ap_ifconfig)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        apstat_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    apstat_info = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    apstat_info.append(line[4:])
                else:
                    apstat_info.append(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(apstat_info) > 0:
        auth = AUTHMODE_DICT[int(apstat_info[-2])]
        print('AP INFO:')
        print('=' * 70)
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
            'AP ENABLED', 'ESSID', 'CHANNEL', 'AUTHMODE'))
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(
            *apstat_info[:-2], auth))
        print('=' * 70)
        print('{0:^15} | {1:^15} | {2:^15} | {3:^15} '.format(
            'IP', 'SUBNET', 'GATEAWAY', 'DNS'))
        try:
            print('{0:^15} | {1:^15} | {2:^15} | {3:^15}'.format(
                *ast.literal_eval(apstat_info[-1])))
        except Exception as e:
            print(e)
            pass
    sys.exit()

# APCONFIG

elif args.m == 'apconfig':
    essid, passwd = args.ap
    ap_config = "network.WLAN(network.AP_IF).config(essid='{}',authmode=network.AUTH_WPA_WPA2_PSK, password='{}')".format(
        essid, passwd)
    apconfig_cmd = "import network;{};gc.collect()".format(ap_config)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        apconfig_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# APSCAN

elif args.m == 'apscan':
    help_cmd = "import network;network.WLAN(network.AP_IF).status('stations');gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        help_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    ap_scan_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    if line[4:][0] != '[':
                        print(line[4:])
                    else:
                        ap_scan_list.append(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(ap_scan_list) > 0:
        ap_devices = ast.literal_eval(ap_scan_list[0])
        print('Found {} devices:'.format(len(ap_devices)))
        print(ap_devices)
    else:
        print('No device found')
    sys.exit()


# I2C_CONFIG

elif args.m == 'i2c_config':
    i2c_po = {'scl': args.i2c[0], 'sda': args.i2c[1]}
    cmd = "i2c_po = {};gc.collect()".format(i2c_po)
    print('SCL = Pin({}), SDA = Pin({})'.format(args.i2c[0], args.i2c[1]))
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# I2C_SCAN

elif args.m == 'i2c_scan':
    i2c_scl = "i2c_po['scl']"
    i2c_sda = "i2c_po['sda']"
    i2c_cmd = "from machine import I2C,Pin;i2c = I2C(scl=Pin({}),sda=Pin({}));i2c.scan();gc.collect()".format(
        i2c_scl, i2c_sda)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        i2c_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    i2c_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    if line[4:][0] != '[':
                        print(line[4:])
                    else:
                        i2c_info_list.append(line[4:])

                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(i2c_info_list) > 0:
        i2c_devices = ast.literal_eval(i2c_info_list[0])
        print('Found {} devices:'.format(len(i2c_devices)))
        print(i2c_devices)
        print('Hex:')
        print([hex(dev) for dev in i2c_devices])
    else:
        print('No device found')
    sys.exit()

# SPI CONFIG
elif args.m == 'spi_config':
    spi_po = {'sck': args.spi[0], 'miso': args.spi[1],
              'mosi': args.spi[2], 'cs': args.spi[3]}
    cmd = "spi_po = {};gc.collect()".format(spi_po)
    print('SCK = Pin({}), MISO = Pin({}), MOSI = Pin({}), CS = Pin({}))'.format(
        *args.spi))
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()


#  * RTC *

# SET LOCAL TIME

elif args.m == 'set_localtime':
    print('Setting local time: {}'.format(
        datetime.now().strftime("%Y-%m-%d T %H:%M:%S")))
    rtc_cmd = "from machine import RTC;rtc = RTC();"
    wkoy = date.today().isocalendar()[1]
    datetime_local = [val for val in datetime.now().timetuple()[:-3]]
    datetime_tuple = datetime_local[:3]
    datetime_tuple.append(wkoy)
    datetime_final = datetime_tuple + datetime_local[3:] + [0]
    utc_zone_cmd = "rtc.datetime(({}, {}, {}, {}, {}, {}, {}+3, {}));['OK']".format(
        *datetime_final)
    settime_cmd = rtc_cmd + utc_zone_cmd
    dev = W_UPYDEVICE(args.t, args.p)
    dev.output = None
    while dev.output is None:
        dev.cmd(settime_cmd, silent=True)
    dev.cmd_nb('gc.collect()', silent=True)
    print('Done!')
    sys.exit()

# SET NTP TIME
elif args.m == 'set_ntptime':
    print('Setting time UTC+{}'.format(args.utc))
    rtc_cmd = "from machine import RTC;rtc = RTC();"
    ntptime_cmd = "from ntptime import settime;settime();"
    rtc_config = "(year, month, mday, week_of_year, hour, minute, second, milisecond) = rtc.datetime();"
    utc_zone_cmd = "rtc.datetime((year, month, mday, week_of_year,hour+{}, minute, second, milisecond));['OK']".format(
        args.utc)
    settime_cmd = rtc_cmd + ntptime_cmd + rtc_config + utc_zone_cmd
    dev = W_UPYDEVICE(args.t, args.p)
    dev.output = None
    while dev.output is None:
        dev.cmd(settime_cmd, silent=True)
    dev.cmd_nb('gc.collect()', silent=True)
    print('Done!')
    sys.exit()

# GET UPY DEVICE LOCALTIME
elif args.m == 'get_datetime':
    get_time_cmd = "tnow = time.localtime();"
    get_time2_cmd = "[tnow[0],tnow[1],tnow[2],tnow[3],tnow[4],tnow[5]];gc.collect()"
    get_datetime_cmd = get_time_cmd + get_time2_cmd
    dev = W_UPYDEVICE(args.t, args.p)
    dev.output = None
    while dev.output is None:
        dev.cmd(get_datetime_cmd, silent=True)
    formatted_devtime = _ft_datetime(dev.output)
    print('Device RTC time: {}-{}-{} T {}:{}:{}'.format(*formatted_devtime))
    sys.exit()
#############################################
# WLAN UTILS COMMANDS

# WLAN_INIT
elif args.m == 'wlan_init':
    wlan_init_cmd = 'from wifiutils import WIFI_UTIL; u_wlan = WIFI_UTIL(silent=False);gc.collect()'
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        wlan_init_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# WSTA_CONFIG

elif args.m == 'wsta_config':
    essid, passwd = args.wp
    connect_to = "u_wlan.sta_config('{}', '{}')".format(
        essid, passwd)
    conn_cmd = "{};gc.collect()".format(connect_to)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        conn_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# WAP_CONFIG

elif args.m == 'wap_config':
    essid, passwd = args.ap
    connect_to = "u_wlan.ap_config('{}', '{}')".format(
        essid, passwd)
    conn_cmd = "{};gc.collect()".format(connect_to)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        conn_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# WSTA_CONN

elif args.m == 'wsta_conn':
    connect_to = "u_wlan.STA_conn()"
    conn_cmd = "{};gc.collect()".format(connect_to)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        conn_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# WAP_CONN

elif args.m == 'wap_conn':
    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True)
    conn_cmd = "u_wlan.AP_conn()"
    AP_CONN = wsdevice.wr_cmd(conn_cmd, silent=True, long_string=True,
                              rtn_resp=True)
    print(AP_CONN.strip())
    sys.exit()
#############################################

# SD COMMANDS

# SD_ENABLE
elif args.m == 'sd_enable':
    sd_enable_pin_config = "sd_enable=Pin({}, Pin.OUT);".format(args.po[0])
    sd_enable_cmd = "sd_enable.value(not sd_enable.value());print(sd_enable.value());gc.collect()"
    sd_onoff_cmd = sd_enable_pin_config + sd_enable_cmd
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sd_onoff_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    sd_state = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                    sd_state.append(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(sd_state) > 0:
        if ast.literal_eval(sd_state[0]) == 1:
            print('SD ENABLED')
        else:
            print('SD DISABLED')
    sys.exit()

# SD_INIT
elif args.m == 'sd_init':
    spi_sck = "spi_po['sck']"
    spi_miso = "spi_po['miso']"
    spi_mosi = "spi_po['mosi']"
    spi_cs = "spi_po['cs']"
    spi_init = "spi = machine.SPI(1, baudrate=10000000, sck=Pin({}), mosi=Pin({}), miso=Pin({}));".format(
        spi_sck, spi_mosi, spi_miso)
    cs_init = "cs = Pin({}, Pin.OUT);".format(spi_cs)
    # mount sd
    sd_card_init = "import sdcard;sd = sdcard.SDCard(spi, cs);time.sleep_ms(1000);"
    sd_mount = "uos.mount(sd, '/sd');print(uos.listdir('/'))"
    sd_final_cmd = spi_init + cs_init + sd_card_init + sd_mount
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sd_final_cmd, args.t, args.p)
    print('Initialzing SD card...')
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Done!')
    sys.exit()

# SD_DEINIT

elif args.m == 'sd_deinit':
    sd_deinit_and_disable_cmd = "uos.umount('/sd');sd_enable.off();gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sd_deinit_and_disable_cmd, args.t, args.p)
    print('Deinitialzing SD card...')
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Done!')
    sys.exit()

# SD_AUTO

elif args.m == 'sd_auto':
    sd_auto_cmd = "import SD_AM;gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sd_auto_cmd, args.t, args.p)
    run_live_cmd = shlex.split(cmd_str)
    print('Autodetect SD Card mode enabled')
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except KeyboardInterrupt:
        try:
            print('...closing...')
            time.sleep(1)
            result = proc.stdout.readlines()
            for message in result:
                print(message[:-1].decode())
            for i in range(10):
                proc.stdout.readlines()
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, args.t, args.p)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
        except KeyboardInterrupt:
            print('...wait for closing...')
            # Flush error keyboardinterrupt
            cmd = ''
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                cmd, args.t, args.p)
            cmd_resp = run_command_rl(cmd_str)
            time.sleep(1)
    print('Done!')
    sys.exit()


#############################################
# SENSOR SPECIFIC COMMANDS

#  * ADC *

# ADC CONFIG
elif args.m == 'adc_config':
    if args.att == 'info':
        print(ATTEN_INFO)
    else:
        if args.b is not None:
            print('Available ADC pins for {} board are : {}'.format(
                args.b, ADC_PINS_DICT[args.b]))
        else:
            adc_pin = args.po[0]
            analog_cmd = "from machine import ADC,Pin;analog_pin = ADC(Pin({}));".format(
                adc_pin)
            analog_atten = "analog_pin.atten({});gc.collect()".format(
                ATTEN_DICT[int(args.att)])
            adc_cmd = analog_cmd + analog_atten
            cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
                adc_cmd, args.t, args.p)
            cmd_resp = run_command_rl(cmd_str)
            resp = cmd_resp[1]
            net_info_list = []
            for line in resp[6:]:
                if line == '### closed ###':
                    pass
                else:
                    try:
                        if line[0] == '>':
                            print(line[4:])
                        else:
                            print(line)

                    except Exception as e:
                        if len(line) == 0:
                            pass
                        else:
                            print(e)
                            pass
            print('Pin {} configured as Analog Input with {} attenuation'.format(
                adc_pin, ATTEN_DICT[int(args.att)][4:]))
        sys.exit()

# ADC READ
elif args.m == 'aread':
    analog_read = "((analog_pin.read())/4095)*3.6;gc.collect()"
    adc_cmd = analog_read
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        adc_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    adclev = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    adclev.append(line[4:])
                else:
                    adclev.append(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(adclev) > 0:
        volts = ast.literal_eval(adclev[0])
        print('Volts: {}'.format(volts))
    sys.exit()

# * ADS *
# ADS_INIT
elif args.m == 'ads_init':
    ads_lib = args.ads
    import_ads_cmd = "from {} import {};import init_ADS as ads;".format(
        args.ads, args.ads.upper())
    print('Initialazing ads...')
    ads_init_cmd = "my_ads = ads.MY_ADS({},ads.i2c,None,channel={});".format(
        args.ads.upper(), args.ch)
    if args.i2c != [22, 23]:
        ads_init_cmd = "{};my_ads = ads.MY_ADS({},{},None,channel={});".format(
            'from machine import I2C', args.ads.upper(), 'I2C(scl=Pin({}), sda=Pin({}))'.format(*args.i2c), args.ch)
    ads_final_init = "my_ads.init();gc.collect()"
    ads_init_cmd_str = import_ads_cmd + ads_init_cmd + ads_final_init
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        ads_init_cmd_str, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# ADS_READ
elif args.m == 'ads_read':
    if args.tm is None:
        cmd = "my_ads.read_V();"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        ads_info_list = []
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        ads_info_list.append(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        if len(ads_info_list) > 0:
            my_read = round(ast.literal_eval(ads_info_list[0]), 2)
            print('{} V'.format(my_read))
            if args.f is not None:
                data_shot = [my_read]
                tag_tstamp = datetime.now().strftime("%H:%M:%S")
                time_stamp = tag_tstamp
                if args.n is not None:
                    tag_tstamp = args.n
                data_shot.append(tag_tstamp)
                header = {'VAR': ['V', 'TS'], 'UNIT': 'Volts'}
                if args.f not in os.listdir():
                    with open(args.f, 'w') as file_log:
                        file_log.write(json.dumps(header))
                        file_log.write('\n')
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                else:
                    with open(args.f, 'a') as file_log:
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                print('Logged at {}'.format(time_stamp))

    elif args.ads == 'test':
        stream_ads = "my_ads.start_send(my_ads.chunk_send_V,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        ch = simple_cmd_r('my_ads.channel')
        print('Streaming ADS: A{} (voltage),fq={}Hz'.format(ch, fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['V'], 'UNIT': 'VOLTS',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            test_stream_chunk(stream_ads, args.t, args.p, 'my_ads',
                              filename=args.f, log=True,
                              r_format='f'*20, nb=80, variables=header['VAR'])
        else:
            header = {'VAR': ['V'], 'UNIT': 'VOLTS',
                      'fq(hz)': fq}
            test_stream_chunk(stream_ads, args.t, args.p, 'my_ads',
                              variables=header['VAR'], r_format='f'*20, nb=80)
    else:
        # Do connect
        stream_ads = "my_ads.start_send(my_ads.chunk_send_V,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        ch = simple_cmd_r('my_ads.channel')
        print('Streaming ADS: A{} (voltage),fq={}Hz'.format(ch, fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['V'], 'UNIT': 'VOLTS',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            get_live_stream_chunk(stream_ads, args.t, args.p, 'my_ads',
                                  filename=args.f, log=True,
                                  r_format='f'*20, nb=80, variables=header['VAR'])
        else:
            header = {'VAR': ['V'], 'UNIT': 'VOLTS',
                      'fq(hz)': fq}
            get_live_stream_chunk(stream_ads, args.t, args.p, 'my_ads',
                                  variables=header['VAR'], r_format='f'*20, nb=80)
    sys.exit()
# * IMU *
# IMU_INIT

elif args.m == 'imu_init':
    imu_lib = args.imu
    import_imu_cmd = "from {} import {};import init_IMU as imu;".format(
        args.imu, args.imu.upper())
    print('Initialazing imu...')
    imu_init_cmd = "my_imu = imu.MY_IMU({},imu.i2c,None);".format(
        args.imu.upper())
    if args.i2c != [22, 23]:
        imu_init_cmd = "{};my_imu = imu.MY_IMU({},{},None);".format(
            'from machine import I2C', args.imu.upper(), 'I2C(scl=Pin({}), sda=Pin({}))'.format(*args.i2c))
    imu_final_init = "my_imu.init()"
    imu_init_cmd_str = import_imu_cmd + imu_init_cmd + imu_final_init
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        imu_init_cmd_str, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

#  IMUACC

elif args.m == 'imuacc':
    if args.tm is None:
        cmd = "my_imu.read_acc()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        acc_info_list = []
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        acc_info_list.append(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        if len(acc_info_list) > 0:
            print('X:{},Y:{},Z:{}'.format(*ast.literal_eval(acc_info_list[0])))
            if args.f is not None:
                data_shot = [*ast.literal_eval(acc_info_list[0])]
                tag_tstamp = datetime.now().strftime("%H:%M:%S")
                time_stamp = tag_tstamp
                if args.n is not None:
                    tag_tstamp = args.n
                data_shot.append(tag_tstamp)
                header = {'VAR': ['X', 'Y', 'Z', 'TS'], 'UNIT': 'g=-9.8m/s^2'}
                if args.f not in os.listdir():
                    with open(args.f, 'w') as file_log:
                        file_log.write(json.dumps(header))
                        file_log.write('\n')
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                else:
                    with open(args.f, 'a') as file_log:
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                print('Logged at {}'.format(time_stamp))
    elif args.imu == 'test':
        # Do connect
        stream_acc = "my_imu.start_send(my_imu.sample_send_acc,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming IMU ACCELEROMETER: X, Y, Z (g=-9.8m/s^2),fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'g=-9.8m/s^2',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            test_stream(stream_acc, args.t, args.p, 'my_imu',
                        filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'g=-9.8m/s^2',
                      'fq(hz)': fq}
            test_stream(stream_acc, args.t, args.p, 'my_imu',
                        variables=header['VAR'])

    else:
        # Do connect
        stream_acc = "my_imu.start_send(my_imu.sample_send_acc,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming IMU ACCELEROMETER: X, Y, Z (g=-9.8m/s^2),fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'g=-9.8m/s^2',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            get_live_stream(stream_acc, args.t, args.p, 'my_imu',
                            filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'g=-9.8m/s^2',
                      'fq(hz)': fq}
            get_live_stream(stream_acc, args.t, args.p, 'my_imu',
                            variables=header['VAR'])

    sys.exit()

# IMUACC SD
elif args.m == 'imuacc_sd':
    # Do connect
    fq = 1/(args.tm/1000)
    header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'g=-9.8m/s^2',
              'fq(hz)': fq}
    stream_acc = "my_imu.start_send_SD(my_imu.sample_send_acc_SD,'ACC','{}',timeout={})".format(
        header['UNIT'], args.tm)
    # run_live(stream_acc, args.t, args.p)
    # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
    print('Streaming IMU ACCELEROMETER: X, Y, Z (g=-9.8m/s^2),fq={}Hz'.format(fq))
    get_live_stream(stream_acc, args.t, args.p, 'my_imu',
                    variables=header['VAR'])
# IMUGY

elif args.m == 'imugy':
    if args.tm is None:
        cmd = "my_imu.read_gy()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        acc_info_list = []
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        acc_info_list.append(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        if len(acc_info_list) > 0:
            print('X:{},Y:{},Z:{}'.format(*ast.literal_eval(acc_info_list[0])))
            if args.f is not None:
                data_shot = [*ast.literal_eval(acc_info_list[0])]
                tag_tstamp = datetime.now().strftime("%H:%M:%S")
                time_stamp = tag_tstamp
                if args.n is not None:
                    tag_tstamp = args.n
                data_shot.append(tag_tstamp)
                header = {'VAR': ['X', 'Y', 'Z', 'TS'], 'UNIT': 'deg/s'}
                if args.f not in os.listdir():
                    with open(args.f, 'w') as file_log:
                        file_log.write(json.dumps(header))
                        file_log.write('\n')
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                else:
                    with open(args.f, 'a') as file_log:
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                print('Logged at {}'.format(time_stamp))
    else:
        # Do connect
        stream_ = "my_imu.start_send(my_imu.sample_send_gy,timeout={})".format(
            args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming IMU GYRO: X, Y, Z (deg/s),fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'deg/s',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            get_live_stream(stream_, args.t, args.p, 'my_imu',
                            filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'deg/s',
                      'fq(hz)': fq}
            get_live_stream(stream_, args.t, args.p, 'my_imu',
                            variables=header['VAR'])

    sys.exit()

# IMUMAG

elif args.m == 'imumag':
    if args.tm is None:
        cmd = "my_imu.read_mag()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        acc_info_list = []
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        acc_info_list.append(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        if len(acc_info_list) > 0:
            print('X:{},Y:{},Z:{}'.format(*ast.literal_eval(acc_info_list[0])))
            if args.f is not None:
                data_shot = [*ast.literal_eval(acc_info_list[0])]
                tag_tstamp = datetime.now().strftime("%H:%M:%S")
                time_stamp = tag_tstamp
                if args.n is not None:
                    tag_tstamp = args.n
                data_shot.append(tag_tstamp)
                header = {'VAR': ['X', 'Y', 'Z', 'TS'], 'UNIT': 'gauss'}
                if args.f not in os.listdir():
                    with open(args.f, 'w') as file_log:
                        file_log.write(json.dumps(header))
                        file_log.write('\n')
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                else:
                    with open(args.f, 'a') as file_log:
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                print('Logged at {}'.format(time_stamp))
    else:
        # Do connect
        stream_ = "my_imu.start_send(my_imu.sample_send_mag,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming IMU MAGNETOMETER: X, Y, Z (gauss),fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'gauss',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            get_live_stream(stream_, args.t, args.p, 'my_imu',
                            filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['X', 'Y', 'Z'], 'UNIT': 'gauss',
                      'fq(hz)': fq}
            get_live_stream(stream_, args.t, args.p, 'my_imu',
                            variables=header['VAR'])

    sys.exit()

# * BME280 *
# BME_INIT

elif args.m == 'bme_init':
    bme_lib = args.bme
    import_bme_cmd = "from {} import {};import init_BME280 as bme280;".format(
        args.bme, args.bme.upper())
    print('Initialazing bme280...')
    bme_init_cmd = "my_bme = bme280.MY_BME280({},bme280.i2c);".format(
        args.bme.upper())
    if args.i2c != [22, 23]:
        bme_init_cmd = "{};my_bme = bme280.MY_BME280({},{});".format(
            'from machine import I2C', args.bme.upper(), 'I2C(scl=Pin({}), sda=Pin({}))'.format(*args.i2c))
    bme_final_init = "my_bme.init()"
    bme_init_cmd_str = import_bme_cmd + bme_init_cmd + bme_final_init
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        bme_init_cmd_str, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

#  BME_READ

elif args.m == 'bme_read':
    if args.tm is None:
        cmd = "my_bme.read_values()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        bme_info_list = []
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        bme_info_list.append(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        if len(bme_info_list) > 0:
            print('{} C, {} Pa , {} % RH '.format(
                *ast.literal_eval(bme_info_list[0])))
            if args.f is not None:
                data_shot = [*ast.literal_eval(bme_info_list[0])]
                tag_tstamp = datetime.now().strftime("%H:%M:%S")
                time_stamp = tag_tstamp
                if args.n is not None:
                    tag_tstamp = args.n
                data_shot.append(tag_tstamp)
                header = {
                    'VAR': ['Temp(C)', 'Pressure(Pa)', 'RH(%)', 'TS'], 'UNIT': 'T: C; P: Pa; RH: %'}
                if args.f not in os.listdir():
                    with open(args.f, 'w') as file_log:
                        file_log.write(json.dumps(header))
                        file_log.write('\n')
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                else:
                    with open(args.f, 'a') as file_log:
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                print('Logged at {}'.format(time_stamp))
    elif args.bme == 'test':
        # Do connect
        stream_bme = "my_bme.start_send(my_bme.sample_send_data,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming BME280: Temp (C), Pressure (Pa), Rel. Hummidity (%) ,fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['Temp(C)', 'Pressure(Pa)', 'RH(%)', 'TS'], 'UNIT': 'T: C; P: Pa; RH: %',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            test_stream(stream_bme, args.t, args.p, 'my_bme',
                        filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['Temp(C)', 'Pressure(Pa)', 'RH(%)', 'TS'], 'UNIT': 'T: C; P: Pa; RH: %',
                      'fq(hz)': fq}
            test_stream(stream_bme, args.t, args.p, 'my_bme',
                        variables=header['VAR'])

    else:
        # Do connect
        stream_bme = "my_bme.start_send(my_bme.sample_send_data,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming BME280: Temp (C), Pressure (Pa), Rel. Hummidity (%) ,fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['Temp(C)', 'Pressure(Pa)', 'RH(%)'], 'UNIT': 'T: C; P: Pa; RH: %',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            get_live_stream(stream_bme, args.t, args.p, 'my_bme',
                            filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['Temp(C)', 'Pressure(Pa)', 'RH(%)'], 'UNIT': 'T: C; P: Pa; RH: %',
                      'fq(hz)': fq}
            get_live_stream(stream_bme, args.t, args.p, 'my_bme',
                            variables=header['VAR'])

    sys.exit()

# * INA219 *
# INA_INIT

elif args.m == 'ina_init':
    ina_lib = args.ina
    import_ina_cmd = "from {} import {};import init_INA219 as ina219;".format(
        args.ina, args.ina.upper())
    print('Initialazing ina219...')
    ina_init_cmd = "my_ina = ina219.MY_INA219({},ina219.i2c);".format(
        args.ina.upper())
    if args.i2c != [22, 23]:
        ina_init_cmd = "{};my_ina = ina219.MY_INA219({},{});".format(
            'from machine import I2C', args.ina.upper(), 'I2C(scl=Pin({}), sda=Pin({}))'.format(*args.i2c))
    ina_final_init = "my_ina.init()"
    ina_init_cmd_str = import_ina_cmd + ina_init_cmd + ina_final_init
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        ina_init_cmd_str, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    net_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

#  INA_READ

elif args.m == 'ina_read':
    if args.tm is None:
        cmd = "my_ina.read_values()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        ina_info_list = []
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        ina_info_list.append(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        if len(ina_info_list) > 0:
            print('{} V, {} mA , {} mW '.format(
                *ast.literal_eval(ina_info_list[0])))
            if args.f is not None:
                data_shot = [*ast.literal_eval(ina_info_list[0])]
                tag_tstamp = datetime.now().strftime("%H:%M:%S")
                time_stamp = tag_tstamp
                if args.n is not None:
                    tag_tstamp = args.n
                data_shot.append(tag_tstamp)
                header = {'VAR': [
                    'Voltage(V)', 'Current(mA)', 'Power(mW)', 'TS'], 'UNIT': 'V: v; C: mA; P: mW'}
                if args.f not in os.listdir():
                    with open(args.f, 'w') as file_log:
                        file_log.write(json.dumps(header))
                        file_log.write('\n')
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                else:
                    with open(args.f, 'a') as file_log:
                        file_log.write(json.dumps(dict(zip(header['VAR'],
                                                           data_shot))))
                        file_log.write('\n')
                print('Logged at {}'.format(time_stamp))
    elif args.ina == 'test':
        # Do connect
        stream_ina = "my_ina.start_send(my_ina.sample_send_data,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming ina219: Volts (V), Current (mA), Power (mW) ,fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['Voltage(V)', 'Current(mA)', 'Power(mW)'], 'UNIT': 'V: v; C: mA; P: mW',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            test_stream(stream_ina, args.t, args.p, 'my_ina',
                        filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['Voltage(V)', 'Current(mA)', 'Power(mW)'], 'UNIT': 'V: v; C: mA; P: mW',
                      'fq(hz)': fq}
            test_stream(stream_ina, args.t, args.p, 'my_ina',
                        variables=header['VAR'])

    else:
        # Do connect
        stream_ina = "my_ina.start_send(my_ina.sample_send_data,timeout={})".format(
            args.tm)
        # run_live(stream_acc, args.t, args.p)
        # "imu.stream_acc(soc=cli_soc, timeout={})".format(args.tm)
        fq = 1/(args.tm/1000)
        print('Streaming ina219: Volts (V), Current (mA), Power (mW) ,fq={}Hz'.format(fq))
        if args.f is not None:
            args.f = lognow(args.f, args.m)
            print('Saving file {} ...'.format(args.f))
            header = {'VAR': ['Voltage(V)', 'Current(mA)', 'Power(mW)'], 'UNIT': 'V: v; C: mA; P: mW',
                      'fq(hz)': fq}
            with open(args.f, 'w') as file_log:
                file_log.write(json.dumps(header))
                file_log.write('\n')
            get_live_stream(stream_ina, args.t, args.p, 'my_ina',
                            filename=args.f, log=True, variables=header['VAR'])
        else:
            header = {'VAR': ['Voltage(V)', 'Current(mA)', 'Power(mW)'], 'UNIT': 'V: v; C: mA; P: mW',
                      'fq(hz)': fq}
            get_live_stream(stream_ina, args.t, args.p, 'my_ina',
                            variables=header['VAR'])

    sys.exit()

# INA_BATT
elif args.m == 'ina_batt':
    print('\n')
    print(' {:>15}'.format('Battery Life expectancy profiling... '))
    cmd = "my_ina.batt_ts_raw()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    ina_info_list = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    ina_info_list.append(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(ina_info_list) > 0:
        result_dict = json.loads(ast.literal_eval(ina_info_list[0]))
        volts_v, current_v, power_v = result_dict['V'], result_dict['C'], result_dict['P']
        volts = sum(volts_v)/len(volts_v)
        current = sum(current_v)/len(current_v)
        power = sum(power_v)/len(power_v)
        print('\n')
        print('{0:^15} {1:^15}'.format(
            '', '{}  {:>15}  {:>15}'.format('VOLTAGE', 'CURRENT', 'POWER')))
        print('  {0:>15}'.format('='*60))
        print('{0:^15} {1:^15}'.format('| Average |',
                                       '{:.2f} V {:>15.2f} mA  {:>15.2f} mW '.format(volts, current, power)))
        print('{0:^15}'.format('-'*len('| Average |')))
        print('{0:^15} {1:^15}'.format('|   MAX   |', '{:.2f} V {:>15.2f} mA  {:>15.2f} mW '.format(
            max(volts_v), max(current_v), max(power_v))))
        print('{0:^15}'.format('-'*len('| Average |')))
        print('{0:^15} {1:^15}'.format('|   MIN   |', '{:.2f} V {:>15.2f} mA  {:>15.2f} mW '.format(
            min(volts_v), min(current_v), min(power_v))))
        print('{0:^15}'.format('-'*len('| Average |')))
        print('\n')
        if current > 0:
            state = 'Discharging'
        else:
            state = 'Charging'
        percentage = round((volts-3.3)/(4.23-3.4)*100, 1)
        batt_le_full = (args.batt[0]/current)*0.70
        batt_le_now = round((batt_le_full * percentage)/100, 2)
        header = ' | {0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^20}  |'.format(
            'CAPACITY (mAh)', 'VOLTAGE (V)', 'LEVEL (%)', 'STATE', 'TIME LEFT (Hours)')
        print('  {0:^15}   {1:^15}   {2:^15}   {3:^15}   {4:^20}  '.format(
            '', '', 'BATTERY INFO', '', ''))
        print(' {0:>15}'.format('='*(len(header)-1)))
        print(header)
        print(' {0:>15}'.format('-'*(len(header)-1)))
        # print('| {0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^20} |'.format('', '', '', '', ''))
        print(' | {0:^15} | {1:^15.2f} | {2:^15} | {3:^15} | {4:^20}  |'.format(
            args.batt[0], volts, percentage, state, batt_le_now))
        # print('| {0:^15} | {1:^15} | {2:^15} | {3:^15} | {4:^20} |'.format('', '', '', '', ''))
        # print(' {0:>15}'.format('='*len(header)))
        print('\n')
        # if args.f is not None:
        #     data_shot = [*ast.literal_eval(ina_info_list[0])]
        #     tag_tstamp = datetime.now().strftime("%H:%M:%S")
        #     time_stamp = tag_tstamp
        #     if args.n is not None:
        #         tag_tstamp = args.n
        #     data_shot.append(tag_tstamp)
        #     header = {'VAR': ['Voltage(V)', 'Current(mA)', 'Power(mW)', 'TS'], 'UNIT': 'V: v; C: mA; P: mW'}
        #     if args.f not in os.listdir():
        #         with open(args.f, 'w') as file_log:
        #             file_log.write(json.dumps(header))
        #             file_log.write('\n')
        #             file_log.write(json.dumps(dict(zip(header['VAR'],
        #                                                data_shot))))
        #             file_log.write('\n')
        #     else:
        #         with open(args.f, 'a') as file_log:
        #             file_log.write(json.dumps(dict(zip(header['VAR'],
        #                                                data_shot))))
        #             file_log.write('\n')
        #     print('Logged at {}'.format(time_stamp))
#############################################
# * DAC *

# DAC CONFIG
elif args.m == 'dac_config':
    if args.b is not None:
        print('Available DAC pins for {} board are : {}'.format(
            args.b, ADC_PINS_DICT[args.b]))
    else:
        dac_pin = args.po[0]
        analog_cmd = "from machine import DAC;analogdac_pin = DAC(Pin({}));".format(
            dac_pin)
        dac_cmd = analog_cmd
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            dac_cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        print(line[4:])
                    else:
                        print(line)

                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        print('Pin {} configured as Analog Output'.format(
            dac_pin))
    sys.exit()

# DAC WRITE ## 8 BITS, 0-255
elif args.m == 'dac_write':
    val_from_v = int((float(args.sig[0])/3.3)*255)
    analog_write = "analogdac_pin.write({});gc.collect()".format(val_from_v)
    dac_cmd = analog_write
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        dac_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()


# DAC_SIG

elif args.m == 'dac_sig':
    cmds = ['start', 'stop', 'mod']
    if args.sig[0] not in cmds:
        signal_class = "from dac_signal_gen import SIGNAL_GENERATOR;"
        signal_cmd = "sig=SIGNAL_GENERATOR(analogdac_pin,'{}',{},{})".format(
            *args.sig)
        conf_dac_sig = signal_class + signal_cmd
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            conf_dac_sig, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        print(line[4:])
                    else:
                        print(line)
                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        print('Signal type {} with Amplitude {} V and fq {} Hz configured'.format(
            *args.sig))
    elif args.sig[0] == cmds[0]:
        signal_cmd = "sig.start()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            signal_cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        print(line[4:])
                    else:
                        print(line)
                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        print('Signal started!')

    elif args.sig[0] == cmds[1]:
        signal_cmd = "sig.stop()"
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            signal_cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        print(line[4:])
                    else:
                        print(line)
                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        print('Signal stopped!')

    elif args.sig[0] == cmds[2]:
        signal_cmd = "sig.modsig({},{})".format(args.sig[1], args.sig[2])
        cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
            signal_cmd, args.t, args.p)
        cmd_resp = run_command_rl(cmd_str)
        resp = cmd_resp[1]
        for line in resp[6:]:
            if line == '### closed ###':
                pass
            else:
                try:
                    if line[0] == '>':
                        print(line[4:])
                    else:
                        print(line)
                except Exception as e:
                    if len(line) == 0:
                        pass
                    else:
                        print(e)
                        pass
        print('Signal modified to Amplitude: {} V, fq: {} Hz'.format(
            args.sig[1], args.sig[2]))

    sys.exit()

#############################################
# * BUZZER *
# BUZZ CONFIG
elif args.m == 'buzz_config':
    BUZZ_pin = args.po[0]
    buzz_cmd = "from buzzertools import BUZZER;my_buzz = BUZZER({});".format(
        BUZZ_pin)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        buzz_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Pin {} configured as PWM to drive the buzzer'.format(
        BUZZ_pin))
    sys.exit()

# BUZZ SET_ALARM
elif args.m == 'buzz_set_alarm':
    if len(args.at) < 3:
        hour, minute = args.at
        seconds = 0
    else:
        hour, minute, seconds = args.at
    buzz_cmd = "my_buzz.set_alarm_at({},{},{});".format(hour, minute, seconds)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        buzz_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Alarm set at {}:{}:{}'.format(hour, minute, seconds))
    sys.exit()

# BUZZ INTERRUPT
elif args.m == 'buzz_interrupt':
    buzz_cmd = "my_buzz.active_button({},{});".format(*args.po)
    if args.md is not None:
        if args.md[0] == 'rev':
            buzz_cmd = "my_buzz.active_button_rev({},{});".format(*args.po)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        buzz_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Button interrupt set at Pins; {},{}'.format(*args.po))
    sys.exit()

# BUZZ BEEP
elif args.m == 'buzz_beep':
    buzz_cmd = "my_buzz.buzz_beep({},{},{},{});".format(*args.opt)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        buzz_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Beep! '*args.opt[1])
    sys.exit()

#############################################
#  * MOTORS *

#  * SERVO *
# SERVO_CONFIG
elif args.m == 'servo_config':
    servo_pin = args.po[0]
    servo_cmd = "from servo import Servo;my_servo = Servo(Pin({}));".format(
        servo_pin)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        servo_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Pin {} configured as PWM to drive the Servo motor'.format(
        servo_pin))
    sys.exit()

elif args.m == 'servo_angle':
    servo_angle = args.opt[0]
    servo_cmd = "my_servo.write_angle({});".format(
        servo_angle)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        servo_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Servo moved to {} degrees!'.format(
        servo_angle))
    sys.exit()


# * DC MOTOR *

# DCMOTOR_CONFIG

elif args.m == 'dcmotor_config':
    dir_pin, oppo_pin = args.po
    dcmotor_cmd = "from dcmotor import DCMOTOR;my_dcmotor = DCMOTOR({},{});".format(
        dir_pin, oppo_pin)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        dcmotor_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('DC motor configured: Direction Pin:{}, Opposite direction Pin: {}'.format(
        dir_pin, oppo_pin))
    sys.exit()

# DCMOTOR_MOVE
elif args.m == 'dcmotor_move':
    dcmotor_dir_dic = {'R': 0, 'L': 1}
    dcmotor_direction, velocity = args.to
    dcmotor_cmd = "my_dcmotor.move({},{});".format(
        dcmotor_dir_dic[dcmotor_direction], velocity)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        dcmotor_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('DC motor moving to {}!'.format(dcmotor_direction))
    sys.exit()

# DCMOTOR_STOP
elif args.m == 'dcmotor_stop':
    dcmotor_cmd = "my_dcmotor.stop();"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        dcmotor_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('DC motor stopped')
    sys.exit()
# * STEPPER MOTOR *

# STEPPER_CONFIG

elif args.m == 'stepper_config':
    dir_pin, step_pin = args.po
    stepper_cmd = "from stepper import STEPPER;my_stepper = STEPPER({},{});".format(
        dir_pin, step_pin)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        stepper_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Stepper motor configured: Direction Pin:{}, Step Pin: {}'.format(
        dir_pin, step_pin))
    sys.exit()

# STEPPER_MOVE

elif args.m == 'stepper_move':
    step_dir_dic = {'R': 0, 'L': 1}
    step_direction, velocity, steps = args.to
    stepper_cmd = "my_stepper.move_n_steps({},{},{});".format(
        step_dir_dic[step_direction], velocity, steps)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        stepper_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('Stepper moved {} steps to {} !'.format(
        steps, step_direction))
    sys.exit()


#############################################
#  * NETWORKING *

#  * MQTT *

# MQTT_CONFIG

elif args.m == 'mqtt_config':
    if len(args.client) < 4:
        id, b_addr = args.client
        client_cmd = "from mqtt_client import mqtt_client ;my_mqtt = mqtt_client('{}','{}');".format(
            id, b_addr)
    else:
        id, b_addr, user, passwd = args.client
        client_cmd = "from mqtt_client import mqtt_client ;my_mqtt = mqtt_client('{}','{}',user = '{}', password = '{}');".format(
            id, b_addr, user, passwd)

    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        client_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('MQTT Client configured: ID: {}, BROKER: {}'.format(
        id, b_addr))
    sys.exit()

# MQTT_CONN
elif args.m == 'mqtt_conn':
    conn_cmd = "my_mqtt.connect();my_mqtt.set_def_callback();gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        conn_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('MQTT Client connected!')
    sys.exit()

# MQTT_SUB
elif args.m == 'mqtt_sub':
    sub_cmd = "my_mqtt.subs('{}');gc.collect()".format(*args.to)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sub_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    print('MQTT Client subscribed to TOPIC: {}'.format(*args.to))
    sys.exit()

# MQTT_PUB
elif args.m == 'mqtt_pub':
    if len(args.to) > 1:
        pub_cmd = "my_mqtt.pub(topic='{}',paylod='{}');gc.collect()".format(
            *args.to)
    else:
        pub_cmd = "my_mqtt.pub('{}');gc.collect()".format(*args.to)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        pub_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(args.to) > 1:
        print('MQTT Client published message: {} to TOPIC: {}'.format(*args.to))
    else:
        print('MQTT Client published the message: {}'.format(*args.to))
    sys.exit()

# MQTT_CHECK

elif args.m == 'mqtt_check':
    check_cmd = "my_mqtt.check_msg();gc.collect()"
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        check_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    print(line[4:])
                else:
                    print(line)

            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    sys.exit()

# * SOCKETS *

# SOCLI_INIT
elif args.m == 'socli_init':
    if len(args.server) > 2:
        host, port, buff = args.server
        socli_init_cmd = "my_cli=socket_client('{}',{},{})".format(
            host, port, buff)
    else:
        host, port = args.server
        socli_init_cmd = "my_cli=socket_client('{}',{})".format(host, port)
    socli_imp_cmd = "from socket_client_server import socket_client;"
    socli_comp_cmd = '{}{}'.format(socli_imp_cmd, socli_init_cmd)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        socli_comp_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('Initialized client socket to connect to server :{} on port {}'.format(host, port))
    sys.exit()

# SOCLI_CONN
elif args.m == 'socli_conn':
    socli_conn_cmd = "my_cli.connect_SOC();"
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        socli_conn_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('Client connected!')
    sys.exit()

# SOCLI_SEND
elif args.m == 'socli_send':
    socli_send_cmd = "my_cli.send_message('{}');".format(args.n)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        socli_send_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('Message sent!')
    sys.exit()

# SOCLI_RECV
elif args.m == 'socli_recv':
    socli_recv_cmd = "my_cli.recv_message();"
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        socli_recv_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    sys.exit()

# SOCLI_CLOSE
elif args.m == 'socli_close':
    socli_close_cmd = "my_cli.cli_soc.close();"
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        socli_close_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('Client Socket closed!')
    sys.exit()

# SOSRV_INIT
elif args.m == 'sosrv_init':
    if len(args.server) > 1:
        port, buff = args.server
        sosrv_init_cmd = "my_serv=socket_server({},{})".format(port, buff)
    else:
        port = args.server[0]
        sosrv_init_cmd = "my_serv=socket_server({})".format(port)
    sosrv_imp_cmd = "from socket_client_server import socket_server;"
    sosrv_comp_cmd = '{}{}'.format(sosrv_imp_cmd, sosrv_init_cmd)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sosrv_comp_cmd, args.t, args.p)
    cmd_resp = run_command_rl(run_cmd_str)
    resp = cmd_resp[1]
    serv_info = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    serv_info.append(line[4:])
                else:
                    serv_info.append(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(serv_info) > 0:
        print('Server initialized. IP: {} PORT:{}'.format(serv_info[0], port))
    sys.exit()

# SOSRV_START
elif args.m == 'sosrv_start':
    sosrv_start_cmd = "my_serv.start_SOC();"
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sosrv_start_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    sys.exit()

# SOSRV_SEND
elif args.m == 'sosrv_send':
    sosrv_send_cmd = "my_serv.send_message('{}');".format(args.n)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sosrv_send_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('Message sent!')
    sys.exit()

# SOSRV_RECV
elif args.m == 'sosrv_recv':
    sosrv_recv_cmd = "my_serv.recv_message();"
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sosrv_recv_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    sys.exit()

# SOSRV_CLOSE
elif args.m == 'sosrv_close':
    sosrv_close_cmd = "my_serv.serv_soc.close();"
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        sosrv_close_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    print('Server Socket closed!')
    sys.exit()

# * REQUEST *
# RGET_JSON
elif args.m == 'rget_json':
    rq_import_cmd = "import urequests as requests;"
    rq_query_cmd = "resp=requests.get('{}');".format(args.f)
    rq_json_cmd = "resp.json()"
    rq_comp_cmd = "{}{}{}".format(rq_import_cmd, rq_query_cmd, rq_json_cmd)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        rq_comp_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    sys.exit()

# RGET_TEXT
elif args.m == 'rget_text':
    rq_import_cmd = "import urequests as requests;"
    rq_query_cmd = "resp=requests.get('{}');".format(args.f)
    rq_text_cmd = "resp.text"
    rq_comp_cmd = "{}{}{}".format(rq_import_cmd, rq_query_cmd, rq_text_cmd)
    run_cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        rq_comp_cmd, args.t, args.p)
    run_live_cmd = shlex.split(run_cmd_str)
    try:
        proc = subprocess.Popen(
            run_live_cmd, stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT)
        for i in range(6):
            proc.stdout.readline()
        while proc.poll() is None:
            resp = proc.stdout.readline()[:-1].decode()
            if len(resp) > 0:
                if resp[0] == '>':
                    print(resp[4:])
                else:
                    print(resp)
            else:
                print(resp)
    except Exception as e:
        print(e)

    sys.exit()
#############################################
#  * PORT SPECIFIC COMMANDS *

# BATTERY

elif args.m == 'battery':
    batt_cmd = "from machine import ADC;bat = ADC(Pin(35));bat.atten(ADC.ATTN_11DB);((bat.read()*2)/4095)*3.6;gc.collect()"
    info_cmd = 'import uos;uos.uname();gc.collect()'

    wsdevice = BASE_WS_DEVICE(args.t, args.p, init=True, ssl=args.wss,
                              auth=args.wss)
    batlev = wsdevice.wr_cmd(batt_cmd, silent=True, rtn_resp=True)
    wsdevice.close_wconn()

    if batlev > 0:
        volts = batlev
        percentage = round((volts - 3.3) / (4.23 - 3.3) * 100, 1)
        print('Battery Voltage : {} V; Level:{} %'.format(
            round(volts, 2), percentage))
    sys.exit()

# SPECS REF

elif args.m == 'specs':
    try:
        board = args.b
        file_board = '{}/{}.config'.format(upydev.__path__[0], board)
        with open(file_board, 'r') as esp32ref:
            ref_dict = json.loads(esp32ref.read())

        print(ref_dict['SPECS'])

    except Exception as e:
        print("""reference board file not found""")
        sys.exit()

# PIN OUT REF

elif args.m == 'pinout':
    try:
        board = args.b
        file_board = '{}/{}.config'.format(upydev.__path__[0], board)
        with open(file_board, 'r') as esp32ref:
            ref_dict = json.loads(esp32ref.read())

        if args.po is not None:
            pin_query = args.po
            for query in pin_query:
                print('PIN: {}: {}'.format(
                    query, ref_dict['PINOUT'][str(query)]))
        else:
            for key in ref_dict['PINOUT']:
                print('PIN: {}: {}'.format(key, ref_dict['PINOUT'][key]))

    except Exception as e:
        print(str(e), ' Pin not Found or')
        print("""reference board file not found""")
        sys.exit()

    sys.exit()

elif args.m == 'pin_status':
    pinlist = "[16, 17, 26, 25, 34, 39, 36, 4, 21, 13, 12, 27, 33, 15, 32, 14, 22, 23, 5, 18, 19]"
    machine_pin = "pins=[machine.Pin(i, machine.Pin.IN) for i in pin_list]"
    status = "dict(zip([str(p) for p in pins],[p.value() for p in pins]))"
    pin_status_cmd = "import machine;pin_list={};{};{};gc.collect()".format(
        pinlist, machine_pin, status)
    cmd_str = 'web_repl_cmd_r -c "{}" -t {} -p {}'.format(
        pin_status_cmd, args.t, args.p)
    cmd_resp = run_command_rl(cmd_str)
    resp = cmd_resp[1]
    pin_status_resp = []
    for line in resp[6:]:
        if line == '### closed ###':
            pass
        else:
            try:
                if line[0] == '>':
                    pin_status_resp.append(line[4:])
                else:
                    pin_status_resp.append(line)
            except Exception as e:
                if len(line) == 0:
                    pass
                else:
                    print(e)
                    pass
    if len(pin_status_resp) > 0:
        pin_dict = ast.literal_eval(pin_status_resp[0])
        if args.po is not None:
            pin_rqst = ['Pin({})'.format(po) for po in args.po]
            for key in pin_dict.keys():
                if key in pin_rqst:
                    if pin_dict[key] == 1:
                        print('{0:^10} | {1:^5} | HIGH'.format(
                            key, pin_dict[key]))
                    else:
                        print('{0:^10} | {1:^5} |'.format(key, pin_dict[key]))

        else:
            for key in pin_dict.keys():
                if pin_dict[key] == 1:
                    print('{0:^10} | {1:^5} | HIGH'.format(key, pin_dict[key]))
                else:
                    print('{0:^10} | {1:^5} |'.format(key, pin_dict[key]))

    sys.exit()

############################################
