├── .github └── workflows │ └── sync_issues.yml ├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── setup.cfg ├── setup.py ├── tox.ini └── wio ├── __init__.py ├── commands ├── __init__.py ├── cmd_call.py ├── cmd_config.py ├── cmd_delete.py ├── cmd_list.py ├── cmd_login.py ├── cmd_setup.py ├── cmd_state.py └── cmd_udp.py ├── config_default.py ├── serial_list.py ├── termui.py ├── udp.py └── wio.py /.github/workflows/sync_issues.yml: -------------------------------------------------------------------------------- 1 | name: Automate Issue Management 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - edited 8 | - assigned 9 | - unassigned 10 | - labeled 11 | - unlabeled 12 | - reopened 13 | 14 | jobs: 15 | add_issue_to_project: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Add issue to GitHub Project 19 | uses: actions/add-to-project@v1.0.2 20 | with: 21 | project-url: https://github.com/orgs/Seeed-Studio/projects/17 22 | github-token: ${{ secrets.ISSUE_ASSEMBLE }} 23 | labeled: bug 24 | label-operator: NOT -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # wio-cli 2 | venv 3 | test 4 | wio/config.py 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | env/ 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *,cover 51 | .hypothesis/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | 60 | # Sphinx documentation 61 | docs/_build/ 62 | 63 | # PyBuilder 64 | target/ 65 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) Copyright (c) <2015 wangtengoo7@gmail.com> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.txt 3 | 4 | # Include the data files 5 | recursive-include data * 6 | 7 | # If using Python 2.6 or less, then have to include package data, even though 8 | # it's already declared in setup.py 9 | # include sample/*.dat 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | Wio Link Command Line Toolset 3 | =============================== 4 | 5 | .. image:: https://img.shields.io/badge/pypi-0.3.2-green.svg 6 | :target: https://pypi.python.org/pypi/wio-cli/ 7 | :alt: Latest Version 8 | 9 | CLI is used to add WioLink and WioNode, list the Wio device and so on. 10 | 11 | Getting Started 12 | =============== 13 | **Note:** If can't find the USB device, should be install USB to Serial driver first, `download here`_ 14 | 15 | .. _download here: https://www.silabs.com/products/mcu/Pages/USBtoUARTBridgeVCPDrivers.aspx 16 | 17 | Wio Link Client can be installed from PyPI using pip:: 18 | 19 | $ pip install wio-cli 20 | 21 | On Python3, use virtualenv to install:: 22 | 23 | $ virtualenv -p python2 env2 24 | $ source env2/bin/activate 25 | $ pip install wio-cli 26 | $ wio --version 27 | 28 | 29 | If you have already installed the library, execute the following command to ensure you’re using the latest library:: 30 | 31 | pip install wio-cli --upgrade 32 | 33 | A list of global options and supported, commands is shown with ``--help``:: 34 | 35 | $ wio --help 36 | 37 | More info, that can be used to get help text for a specific command:: 38 | 39 | $ wio --help 40 | 41 | Getting Started with Wio Link Command Line Toolset: http://www.seeedstudio.com/recipe/1136-getting-started-with-wio-link-command-line-toolset.html) 42 | 43 | Command 44 | ========== 45 | Login with your Wiolink account:: 46 | 47 | $wio login 48 | 49 | Add a new device with USB connect:: 50 | 51 | $ wio setup 52 | 53 | Login state:: 54 | 55 | $ wio state 56 | 57 | example: 58 | $ wio state 59 | email: xxx@xxx.xx 60 | token: 4LxiwvwFAw3C7LiiUQiZh6qOj44tV6KGsXyjp3jVzxx 61 | mserver: https://iot.seeed.cc 62 | mserver_ip: 45.79.4.239 63 | 64 | Displays a list of your devices and their APIs:: 65 | 66 | $ wio list 67 | 68 | example: 69 | $ wio list 70 | |-- home (online) 71 | |-- sn: e3d0dccd4587f40a5d6ffda236755aa4 72 | |-- token: ce140e79f24717ed7d6d44bfb5d848b2 73 | |-- resource url: http://192.168.21.115:8080/v1/node/resources?access_token=ce140e79f24717ed7d6d44bfb5d848b2 74 | |-- well_known: 75 | |-- GET /v1/node/GroveTempHumProD0/humidity -> float humidity 76 | |-- GET /v1/node/GroveTempHumProD0/temperature -> float celsius_degree 77 | |-- GET /v1/node/GroveTempHumProD0/temperature_f -> float fahrenheit_degree 78 | 79 | Request api, return json:: 80 | 81 | $ wio call 82 | 83 | example: 84 | $ wio call c74a110c2e397823f0ce53ef669d5b7f GET /v1/node/GroveMoistureA0/moisture 85 | {u'moisture': 0} 86 | 87 | 88 | Delete a device:: 89 | 90 | $ wio delete 91 | 92 | example: 93 | $wio delete 2885b2cab8abc5fb8e229e4a77bf5e4d 94 | >> Delete device commplete! 95 | 96 | Config your device setting:: 97 | 98 | $wio config --debug [on|off], enable/disable wio debug 99 | $wio config --get-debug, get wio debug status 100 | 101 | Serial port permissions 102 | ========== 103 | 1. now as normal user from terminal: 104 | 105 | $ ls -l /dev/ttyUSB* 106 | 107 | you will get something like: 108 | 109 | crw-rw---- 1 root dialout 188, 0 5 apr 23.01 ttyUSB0 110 | 111 | The "0" might be a different number, or multiple entries might be returned. In the first case the data we need is "uucp", in the second "dialout" (is the group owner of the file. 112 | 113 | 2. Now we just need to add our user to the group: 114 | 115 | $ sudo usermod -a -G group-name username 116 | 117 | where group-name is the data found before, and username is your linux user name. You will need to log out and in again for this change to take effect. such as: 118 | 119 | $ sudo usermod -a -G dialout tengwang 120 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | # This flag says that the code is written to work on both Python 2 and Python 3 | # 3. If at all possible, it is good practice to do this. If you cannot, you 4 | # will need to generate wheels for each Python version that you support. 5 | universal=1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import re 3 | import os 4 | 5 | version = '' 6 | with open('wio/wio.py', 'r') as fd: 7 | version = re.search(r'^version\s*=\s*[\'"]([^\'"]*)[\'"]', 8 | fd.read(), re.MULTILINE).group(1) 9 | 10 | if not version: 11 | raise RuntimeError('Cannot find version information') 12 | 13 | setup( 14 | name='wio-cli', 15 | version=version, 16 | description='Command line for Wio Link', 17 | url='https://github.com/Seeed-Studio/wio-cli', 18 | author='Ten Wong', 19 | author_email='wangtengoo7@gmail.com', 20 | license='MIT', 21 | packages=['wio', 'wio.commands'], 22 | # data_files=[('config', ['wio/config.json'])], 23 | # include_package_data=True, 24 | install_requires=[ 25 | 'click', 26 | 'requests>=2.9.0', 27 | 'pyserial>=3.1.1', 28 | ], 29 | entry_points=''' 30 | [console_scripts] 31 | wio=wio.wio:cli 32 | ''', 33 | ) 34 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # content of: tox.ini , put in same dir as setup.py 2 | [tox] 3 | envlist = py27,py30 4 | [testenv] 5 | /*deps=pytest # install pytest in the venvs*/ 6 | /*commands=py.test # or 'nosetests' or ...*/ 7 | -------------------------------------------------------------------------------- /wio/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/wio-cli/fc187a25b4e2857426e4832b119749e76857899f/wio/__init__.py -------------------------------------------------------------------------------- /wio/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/wio-cli/fc187a25b4e2857426e4832b119749e76857899f/wio/commands/__init__.py -------------------------------------------------------------------------------- /wio/commands/cmd_call.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio import termui 3 | from wio.wio import pass_wio 4 | from wio.wio import verify 5 | 6 | import requests 7 | 8 | @click.command() 9 | @click.argument('token') 10 | @click.argument('method') 11 | @click.argument('endpoint') 12 | # @click.option('--xchange', help='xchange server url') 13 | @pass_wio 14 | def cli(wio, method, endpoint, token,): 15 | ''' 16 | Request api, return json. 17 | 18 | \b 19 | DOES: 20 | Call a api on your devices. 21 | token: device_token 22 | method: GET or POST 23 | endpoint: device_path, such as: /v1/node/GroveTempHumProD0/temperature 24 | wio call 25 | 26 | \b 27 | EXAMPLE: 28 | wio call 98dd464bd268d4dc4cb9b37e4e779313 GET /v1/node/GroveTempHumProD0/temperature 29 | ''' 30 | user_token = wio.config.get("token", None) 31 | api_prefix = wio.config.get("mserver", None) 32 | if not api_prefix or not user_token: 33 | click.echo(click.style('>> ', fg='red') + "Please login, use " + 34 | click.style("wio login", fg='green')) 35 | return 36 | 37 | api = "%s%s?access_token=%s" %(api_prefix, endpoint, token) 38 | 39 | try: 40 | if method == "GET": 41 | r = requests.get(api, timeout=15, verify=verify) 42 | elif method == "POST": 43 | r = requests.post(api, timeout=15, verify=verify) 44 | else: 45 | click.secho(">> METHOD [%s] is wrong, should be GET or POST." %method, fg='red') 46 | return 47 | r.raise_for_status() 48 | json_response = r.json() 49 | except requests.exceptions.HTTPError as e: 50 | if r.status_code == 400: 51 | # error = r.json().get("error", None) 52 | # click.secho(">> %s" %error, fg='red') 53 | click.echo(r.json()) 54 | else: 55 | click.secho(">> %s" %e, fg='red') 56 | return 57 | except Exception as e: 58 | click.secho(">> %s" %e, fg='red') 59 | return 60 | 61 | click.echo(r.json()) 62 | -------------------------------------------------------------------------------- /wio/commands/cmd_config.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio.wio import pass_wio 3 | from wio import udp 4 | 5 | 6 | @click.command() 7 | # @click.option('--APCFG', nargs=2) 8 | @click.option('--get-debug', is_flag=True, help="get debug status") 9 | @click.option('--debug', type=click.Choice(['on', 'off']), help="enable/disable debug") 10 | # @click.option('--VERSION', is_flag=True) 11 | # @click.option('--Blank?', is_flag=True) 12 | # @click.option('-p') #TODO(ten): support serial 13 | @pass_wio 14 | def cli(wio, get_debug, debug): 15 | ''' 16 | Change setting of device. 17 | 18 | \b 19 | DOES: 20 | The config command lets you change setting of device through upd. 21 | 1. Ensure your device is Configure Mode. 22 | 2. Change your computer network to Wio's AP. 23 | 24 | \b 25 | EXAMPLE: 26 | wio config --debug [on|off], enable/disable wio debug 27 | wio config --get-debug, get wio debug status 28 | ''' 29 | if debug: 30 | if debug == "on": 31 | cmd = "ENDEBUG: 1" 32 | elif debug == "off": 33 | cmd = "ENDEBUG: 0" 34 | if not cmd: 35 | return debug_error() 36 | result = udp.send(cmd) 37 | if not result: 38 | return debug_error() 39 | click.echo("Setting success!! Device will reboot!!") 40 | 41 | elif get_debug: 42 | try: 43 | result = udp.udp_debug() 44 | except Exception as e: 45 | return get_debug_error() 46 | 47 | if result == "1": 48 | click.echo("debug: on") 49 | elif result == '0': 50 | click.echo("debug: off") 51 | else: 52 | return get_debug_error() 53 | 54 | else: 55 | click.echo("Note:") 56 | click.echo(" 1. Ensure your device is Configure Mode.") 57 | click.echo(" 2. Change your computer network to Wio's AP.") 58 | click.echo() 59 | click.echo("Use:") 60 | click.echo(" wio config --debug [on|off], enable/disable wio debug") 61 | click.echo(" wio config --get-debug, get wio debug status") 62 | 63 | def debug_error(): 64 | click.echo("Setting failure!!") 65 | error_tip() 66 | 67 | def get_debug_error(): 68 | click.echo("Get debug status failure!!") 69 | error_tip() 70 | 71 | def error_tip(): 72 | click.echo("Note:") 73 | click.echo(" 1. Ensure your device is Configure Mode.") 74 | click.echo(" 2. Change your computer network to Wio's AP.") 75 | -------------------------------------------------------------------------------- /wio/commands/cmd_delete.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio import termui 3 | from wio.wio import pass_wio 4 | from wio.wio import nodes_delete_endpoint 5 | from wio.wio import verify 6 | 7 | import requests 8 | 9 | @click.command() 10 | @click.argument('sn') 11 | @pass_wio 12 | def cli(wio, sn): 13 | ''' 14 | Delete a device. 15 | 16 | \b 17 | DOES: 18 | Delete a device. 19 | sn: device_sn 20 | wio delete 21 | 22 | \b 23 | EXAMPLE: 24 | wio delete 2885b2cab8abc5fb8e229e4a77bf5e4d 25 | ''' 26 | user_token = wio.config.get("token", None) 27 | api_prefix = wio.config.get("mserver", None) 28 | if not api_prefix or not user_token: 29 | click.echo(click.style('>> ', fg='red') + "Please login, use " + 30 | click.style("wio login", fg='green')) 31 | return 32 | 33 | params = {"access_token":user_token, "node_sn":sn} 34 | try: 35 | r = requests.post("%s%s" %(api_prefix, nodes_delete_endpoint), params=params, timeout=10, verify=verify) 36 | r.raise_for_status() 37 | json_response = r.json() 38 | except requests.exceptions.HTTPError as e: 39 | if r.status_code == 400: 40 | error = r.json().get("error", None) 41 | click.secho(">> %s" %error, fg='red') 42 | else: 43 | click.secho(">> %s" %e, fg='red') 44 | return 45 | except Exception as e: 46 | click.secho(">> %s" %e, fg='red') 47 | return 48 | 49 | click.secho('>> Delete device commplete!', fg='white') 50 | -------------------------------------------------------------------------------- /wio/commands/cmd_list.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio import termui 3 | from wio.wio import pass_wio 4 | from wio.wio import node_list_endpoint 5 | from wio.wio import node_resources_endpoint 6 | from wio.wio import well_known_endpoint 7 | from wio.wio import nodes_delete_endpoint 8 | from wio.wio import boards 9 | from wio.wio import verify 10 | 11 | import requests 12 | 13 | @click.command() 14 | @pass_wio 15 | def cli(wio): 16 | ''' 17 | Displays a list of your devices. 18 | 19 | \b 20 | DOES: 21 | Displays a list of your devices, as well as their APIs 22 | 23 | \b 24 | USE: 25 | wio list 26 | ''' 27 | user_token = wio.config.get("token", None) 28 | api_prefix = wio.config.get("mserver", None) 29 | if not api_prefix or not user_token: 30 | click.echo(click.style('>> ', fg='red') + "Please login, use " + 31 | click.style("wio login", fg='green')) 32 | return 33 | 34 | thread = termui.waiting_echo("Retrieving devices...") 35 | thread.daemon = True 36 | thread.start() 37 | params = {"access_token":user_token} 38 | try: 39 | r = requests.get("%s%s" %(api_prefix, node_list_endpoint), params=params, timeout=10, verify=verify) 40 | r.raise_for_status() 41 | json_response = r.json() 42 | except requests.exceptions.HTTPError as e: 43 | thread.stop('') 44 | thread.join() 45 | if r.status_code == 400: 46 | error = r.json().get("error", None) 47 | click.secho(">> %s" %error, fg='red') 48 | else: 49 | click.secho(">> %s" %e, fg='red') 50 | return 51 | except Exception as e: 52 | thread.stop('') 53 | thread.join() 54 | click.secho(">> %s" %e, fg='red') 55 | return 56 | 57 | nodes = json_response.get("nodes", None) 58 | thread.message("Retrieving device APIs...") 59 | node_list = [] 60 | for n in nodes: 61 | if n['name'] == 'node000': 62 | params = {"access_token":user_token, "node_sn":n['node_sn']} 63 | try: 64 | r = requests.post("%s%s" %(api_prefix, nodes_delete_endpoint), params=params, timeout=10, verify=verify) 65 | r.raise_for_status() 66 | json_response = r.json() 67 | except requests.exceptions.HTTPError as e: 68 | thread.stop('') 69 | thread.join() 70 | if r.status_code == 400: 71 | error = r.json().get("error", None) 72 | click.secho(">> %s" %error, fg='red') 73 | else: 74 | click.secho(">> %s" %e, fg='red') 75 | return 76 | except Exception as e: 77 | thread.stop('') 78 | thread.join() 79 | click.secho(">> %s" %e, fg='red') 80 | return 81 | continue 82 | if n["online"]: 83 | params = {"access_token":n["node_key"]} 84 | try: 85 | r = requests.get("%s%s" %(api_prefix, well_known_endpoint), params=params, timeout=15, verify=verify) 86 | r.raise_for_status() 87 | json_response = r.json() 88 | except requests.exceptions.HTTPError as e: 89 | # thread.stop('') 90 | # thread.join() 91 | if r.status_code == 400: 92 | error = r.json().get("error", None) 93 | click.secho(">> %s" %error, fg='red') 94 | else: 95 | click.secho(">> %s" %e, fg='red') 96 | n['well_known'] = [] 97 | # n['onoff'] = 'offline' 98 | except Exception as e: 99 | # thread.stop('') 100 | # thread.join() 101 | click.secho(">> %s" %e, fg='red') 102 | n['well_known'] = [] 103 | else: 104 | well_known = json_response["well_known"] #todo error risk 105 | n['well_known'] = well_known 106 | 107 | n['onoff'] = 'online' 108 | else: 109 | n['well_known'] = [] 110 | n['onoff'] = 'offline' 111 | 112 | n['resources'] = "%s%s?access_token=%s" %(api_prefix, node_resources_endpoint, n['node_key']) 113 | node_list.append(n) 114 | 115 | thread.stop('') 116 | thread.join() 117 | 118 | termui.tree(node_list) 119 | -------------------------------------------------------------------------------- /wio/commands/cmd_login.py: -------------------------------------------------------------------------------- 1 | import click 2 | import sys 3 | import requests 4 | import time 5 | import hashlib 6 | from wio import termui 7 | from wio import config 8 | from wio.wio import pass_wio 9 | from wio.wio import login_endpoint 10 | from wio.wio import ext_user_endpoint 11 | from wio.wio import verify 12 | from wio.wio import choise_server 13 | 14 | 15 | @click.command() 16 | @pass_wio 17 | def cli(wio): 18 | ''' 19 | Login with your Wio account. 20 | 21 | \b 22 | DOES: 23 | Login and save an access token for interacting with your account on the Wio. 24 | 25 | \b 26 | USE: 27 | wio login 28 | ''' 29 | mserver = wio.config.get("mserver", None) 30 | if mserver: 31 | click.echo(click.style('> ', fg='green') + "Current server is: " + 32 | click.style(mserver, fg='green')) 33 | if click.confirm(click.style('Would you like login with a different server?', bold=True), default=False): 34 | choise_server(wio) 35 | else: 36 | choise_server(wio) 37 | 38 | if wio.config.get("server") == 'Customize': 39 | email = click.prompt(click.style('? ', fg='green') + 40 | click.style('Please enter your email address', bold=True), type=str) 41 | password = click.prompt(click.style('? ', fg='green') + 42 | click.style('Please enter your password', bold=True), hide_input=True, type=str) 43 | server_url = wio.config.get("mserver") 44 | 45 | thread = termui.waiting_echo("Sending login details...") 46 | thread.daemon = True 47 | thread.start() 48 | try: 49 | json_response = login_wio(server_url, email, password) 50 | token = json_response['token'] 51 | except Exception as e: 52 | thread.stop('') 53 | thread.join() 54 | click.secho(">> %s" %e, fg='red') 55 | return 56 | else: 57 | token = click.prompt(click.style('? ', fg='green') + 58 | click.style('First get wio user token from ', bold=True) + 59 | click.style('https://wio.seeed.io/login', bold=True, fg='green') + 60 | click.style('\n? ', fg='green') + 61 | click.style('Then enter token', bold=True), type=str) 62 | email = '' 63 | thread = termui.waiting_echo("Checking validity of token...") 64 | thread.daemon = True 65 | thread.start() 66 | try: 67 | check_token(wio.config.get("mserver"), token) 68 | except Exception as e: 69 | thread.stop('') 70 | thread.join() 71 | click.secho(">> %s" %e, fg='red') 72 | return 73 | # res = login_seeed(email, password) #TODO(ten): next time delete 74 | # token = res['data']['token'] 75 | # user_id = res['data']['userid'] 76 | # ext_user(wio.config.get("mserver"), email, user_id, token) 77 | 78 | wio.set_config('email', email) 79 | wio.set_config('token', token) 80 | 81 | thread.stop('') 82 | thread.join() 83 | click.secho("\r> ", fg='green', nl=False) 84 | click.echo("Successfully completed login! Check state, see 'wio state'") 85 | 86 | def check_token(server_url, token): 87 | url = server_url + '/v1/nodes/list?access_token=' + token 88 | try: 89 | r = requests.get(url) 90 | if r.status_code is not 200: 91 | raise ValueError(r.json()['error']) 92 | return True 93 | except: 94 | raise 95 | 96 | def get_sign(url, time): 97 | APPID = config.SEEED_APPID 98 | APPKEY = config.SEEED_APPKEY 99 | COMMON = config.SEEED_COMMON 100 | li = [APPID, APPKEY, COMMON, url, str(time)] 101 | li.sort() 102 | sign = '' 103 | for l in li: 104 | sign += l 105 | h = hashlib.sha1(sign.encode('utf-8')) 106 | return h.hexdigest() 107 | 108 | def login_seeed(email, passwd): 109 | url = "http://bazaar.seeed.cc/api/index.php?r=common/user/login" 110 | timestamp = int(time.time()) 111 | payload = dict( 112 | email=email, 113 | password=passwd, 114 | source='4', 115 | api_key='wiolink', 116 | timestamp = timestamp, 117 | sign = get_sign('r=common/user/login', timestamp)) 118 | r = requests.post(url, data=payload) 119 | res = r.json() 120 | if res.get('errorcode') != 0: 121 | raise Exception('error', res.get('msgs')) 122 | return res 123 | 124 | def ext_user(server_url, email, user_id, token): 125 | payload = dict( 126 | email=email, 127 | bind_id=user_id, 128 | token=token, 129 | bind_region=config.WIO_REGION, 130 | secret=config.WIO_SECKET 131 | ) 132 | r = requests.post("%s%s" %(server_url, ext_user_endpoint), data=payload, 133 | timeout=10, verify=verify) 134 | res = r.json() 135 | if r.status_code != 200: 136 | raise Exception('error', res.get('error')) 137 | return res 138 | 139 | def login_wio(server_url, email, passwd): 140 | payload = dict(email=email, password=passwd) 141 | r = requests.post("%s%s" %(server_url, login_endpoint), data=payload, 142 | timeout=10, verify=verify) 143 | res = r.json() 144 | if r.status_code != 200: 145 | raise Exception('error', res.get('error')) 146 | return res 147 | -------------------------------------------------------------------------------- /wio/commands/cmd_setup.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio import termui 3 | from wio import serial_list 4 | from wio import udp 5 | from wio.wio import pass_wio 6 | from wio.wio import node_list_endpoint 7 | from wio.wio import nodes_create_endpoint 8 | from wio.wio import nodes_rename_endpoint 9 | from wio.wio import boards 10 | from wio.wio import WIO_LINK_V1_0 11 | from wio.wio import WIO_NODE_V1_0 12 | from wio.wio import verify 13 | 14 | import requests 15 | import re 16 | import time 17 | import serial 18 | try: 19 | from urllib.parse import urlparse 20 | except ImportError: 21 | from urlparse import urlparse 22 | 23 | 24 | def get_new(mserver_url, token, board): 25 | '''get node sn and key''' 26 | thread = termui.waiting_echo("Getting message from Server...") 27 | thread.daemon = True 28 | thread.start() 29 | try: 30 | params = {"name":"node000", "board":board, "access_token":token} 31 | r = requests.post("%s%s" %(mserver_url, nodes_create_endpoint), params=params, timeout=10, verify=verify) 32 | r.raise_for_status() 33 | json_response = r.json() 34 | except requests.exceptions.HTTPError as e: 35 | thread.stop('') 36 | thread.join() 37 | if r.status_code == 400: 38 | error = r.json().get("error", None) 39 | click.secho(">> %s" %error, fg='red') 40 | else: 41 | click.secho(">> %s" %e, fg='red') 42 | return None 43 | except Exception as e: 44 | thread.stop('') 45 | thread.join() 46 | click.secho(">> %s" %e, fg='red') 47 | return None 48 | 49 | thread.stop('') 50 | thread.join() 51 | 52 | return json_response 53 | 54 | def check_connect(mserver_url, token, node_sn, d_name): 55 | thread = termui.waiting_echo('') 56 | thread.daemon = True 57 | thread.start() 58 | state_online = False 59 | for i in range(60): 60 | thread.message("The Wio now attempt to connect to Server... [%s]" %(60-i)) 61 | time.sleep(1) 62 | try: 63 | params = {"access_token":token} 64 | r = requests.get("%s%s" %(mserver_url, node_list_endpoint), params=params) 65 | r.raise_for_status() 66 | json_response = r.json() 67 | except Exception as e: 68 | continue 69 | 70 | for n in json_response["nodes"]: 71 | if n["node_sn"] == node_sn and n["online"]: 72 | click.echo(click.style('\r> ', fg='green') + "The Wio connect to Server success. ") 73 | thread.message("Setting Wio name...") 74 | state_online = True 75 | break 76 | if state_online: 77 | break 78 | 79 | if not state_online: 80 | thread.stop('') 81 | thread.join() 82 | click.echo(click.style('\r>> ', fg='red') + "The Wio connect to Server failure.") 83 | click.secho("\n> Please check info you enter, Try again.", fg='white', bold=True) 84 | return None 85 | 86 | try: 87 | params = {"name":d_name,"node_sn":node_sn,"access_token":token} 88 | r = requests.post("%s%s" %(mserver_url, nodes_rename_endpoint), params=params) 89 | r.raise_for_status() 90 | json_response = r.json() 91 | except requests.exceptions.HTTPError as e: 92 | thread.stop('') 93 | thread.join() 94 | if r.status_code == 400: 95 | error = r.json().get("error", None) 96 | click.secho(">> %s" %error, fg='red') 97 | else: 98 | click.secho(">> %s" %e, fg='red') 99 | return None 100 | except Exception as e: 101 | thread.stop('') 102 | thread.join() 103 | click.secho(">> %s" %e, fg='red') 104 | return None 105 | click.echo(click.style('\r> ', fg='green') + "Set Wio name success.") 106 | 107 | thread.stop('') 108 | thread.join() 109 | click.echo() 110 | click.echo(click.style('> ', fg='green') + 111 | click.style("Configuration complete!", fg='white', bold=True)) 112 | 113 | return True 114 | 115 | def upd_send(msvr, msvr_ip, xsvr, xsvr_ip, node_sn, node_key): 116 | click.echo() 117 | click.secho('! ', fg='green', nl=False) 118 | click.echo("PROTIP: " + 119 | click.style("Wireless setup of Wio!", fg='white')) 120 | click.secho('! ', fg='green', nl=False) 121 | click.echo("PROTIP: " + 122 | click.style("You need ", fg='white') + 123 | click.style("manually ", fg='green') + 124 | click.style("change your Wi-Fi network to Wio's network.", fg='white')) 125 | click.secho('! ', fg='green', nl=False) 126 | click.echo("PROTIP: " + 127 | click.style("You will lose your connection to the internet periodically.", fg='white')) 128 | click.echo() 129 | 130 | click.prompt(click.style('? ', fg='green') + 131 | click.style('Please connect to the Wio_* network now. Press enter when ready', bold=True), 132 | default='', show_default=False) 133 | 134 | while 1: 135 | if not click.confirm(click.style('? ', fg='green') + 136 | click.style("Would you like to manually enter your Wi-Fi network configuration?", bold=True), 137 | default=False): 138 | thread = termui.waiting_echo("Asking the Wio to scan for nearby Wi-Fi networks...") 139 | thread.daemon = True 140 | thread.start() 141 | 142 | ssid_list = udp.udp_list() 143 | 144 | if ssid_list: 145 | thread.stop('') 146 | thread.join() 147 | else: 148 | thread.stop("\rsearch failure...\n") 149 | return None 150 | 151 | while 1: 152 | for x in range(len(ssid_list)): 153 | click.echo("%s.) %s" %(x, ssid_list[x])) 154 | click.secho('? ', fg='green', nl=False) 155 | value = click.prompt( 156 | click.style('Please select the network to which your Wio should connect', bold=True), 157 | type=int) 158 | if value >= 0 and value < len(ssid_list): 159 | ssid = ssid_list[value] 160 | break 161 | else: 162 | click.echo(click.style('>> ', fg='red') + "invalid input, range 0 to %s" %(len(ssid_list)-1)) 163 | 164 | ap = ssid 165 | else: 166 | ap = click.prompt(click.style('> ', fg='green') + 167 | click.style('Please enter the SSID of your Wi-Fi network', bold=True), type=str) 168 | 169 | ap_pwd = click.prompt(click.style('> ', fg='green') + 170 | click.style('Please enter your Wi-Fi network password (leave blank for none)', bold=True), 171 | default='', show_default=False) 172 | d_name = click.prompt(click.style('> ', fg='green') + 173 | click.style('Please enter the name of a device will be created', bold=True), type=str) 174 | 175 | click.echo(click.style('> ', fg='green') + "Here's what we're going to send to the Wio:") 176 | click.echo() 177 | click.echo(click.style('> ', fg='green') + "Wi-Fi network: " + 178 | click.style(ap, fg='green', bold=True)) 179 | ap_pwd_p = ap_pwd 180 | if ap_pwd_p == '': 181 | ap_pwd_p = 'None' 182 | click.echo(click.style('> ', fg='green') + "Password: " + 183 | click.style(ap_pwd_p, fg='green', bold=True)) 184 | click.echo(click.style('> ', fg='green') + "Device name: " + 185 | click.style(d_name, fg='green', bold=True)) 186 | click.echo() 187 | 188 | if click.confirm(click.style('? ', fg='green') + 189 | "Would you like to continue with the information shown above?", default=True): 190 | break 191 | 192 | click.echo() 193 | #waiting ui 194 | thread = termui.waiting_echo("Sending Wi-Fi information to device...") 195 | thread.daemon = True 196 | thread.start() 197 | # get version 198 | version = udp.udp_version() 199 | # send udp command 200 | send_flag = False 201 | if version <= 1.1: 202 | cmd = "APCFG: %s\t%s\t%s\t%s\t%s\t%s\t\r\n" %(ap, ap_pwd, node_key, node_sn, xsvr_ip, msvr_ip) 203 | elif version >=1.2: 204 | cmd = "APCFG: %s\t%s\t%s\t%s\t%s\t%s\t\r\n" %(ap, ap_pwd, node_key, node_sn, xsvr, msvr) 205 | else: 206 | cmd = "APCFG: %s\t%s\t%s\t%s\t%s\t%s\t\r\n" %(ap, ap_pwd, node_key, node_sn, xsvr, msvr) 207 | # click.echo(cmd) 208 | result = udp.send(cmd) 209 | thread.stop('') 210 | thread.join() 211 | 212 | if not result: 213 | return None 214 | else: 215 | return {'name':d_name} 216 | 217 | def serial_send(msvr, msvr_ip, xsvr, xsvr_ip, node_sn, node_key, port): 218 | ### check is configure mode? 219 | thread = termui.waiting_echo("Getting device information...") 220 | thread.daemon = True 221 | thread.start() 222 | 223 | flag = False 224 | try: 225 | with serial.Serial(port, 115200, timeout=5) as ser: 226 | cmd = 'Blank?\r\n' 227 | ser.write(cmd.encode('utf-8')) 228 | if 'Node' in ser.readline(): 229 | flag = True 230 | except serial.SerialException as e: 231 | thread.stop('') 232 | thread.join() 233 | click.secho('>> ', fg='red', nl=False) 234 | click.echo(e) 235 | if e.errno == 13: 236 | click.echo("For more information, see https://github.com/Seeed-Studio/wio-cli#serial-port-permissions") 237 | return None 238 | 239 | thread.stop('') 240 | thread.join() 241 | 242 | if flag: 243 | click.secho('> ', fg='green', nl=False) 244 | click.secho("Found Wio.", fg='green', bold=True) 245 | click.echo() 246 | else: 247 | click.secho('> ', fg='green', nl=False) 248 | click.secho("No nearby Wio detected.", fg='white', bold=True) 249 | if click.confirm(click.style('? ', fg='green') + 250 | click.style("Would you like to wait and monitor for Wio entering configure mode", bold=True), 251 | default=True): 252 | 253 | thread = termui.waiting_echo("Waiting for a wild Wio to appear... (press ctrl + C to exit)") 254 | thread.daemon = True 255 | thread.start() 256 | 257 | flag = False 258 | while 1: 259 | with serial.Serial(port, 115200, timeout=5) as ser: 260 | cmd = 'Blank?\r\n' 261 | ser.write(cmd.encode('utf-8')) 262 | if 'Node' in ser.readline(): 263 | flag = True 264 | break 265 | 266 | thread.stop('') 267 | thread.join() 268 | click.secho('> ', fg='green', nl=False) 269 | click.secho("Found Wio.", fg='green', bold=True) 270 | click.echo() 271 | else: 272 | click.secho('> ', fg='green', nl=False) 273 | click.secho("\nQuit wio setup!", bg='white', bold=True) 274 | 275 | while 1: 276 | if not click.confirm(click.style('? ', fg='green') + 277 | click.style("Would you like to manually enter your Wi-Fi network configuration?", bold=True), 278 | default=False): 279 | thread = termui.waiting_echo("Asking the Wio to scan for nearby Wi-Fi networks...") 280 | thread.daemon = True 281 | thread.start() 282 | 283 | flag = False 284 | with serial.Serial(port, 115200, timeout=3) as ser: 285 | cmd = 'SCAN\r\n' 286 | ser.write(cmd.encode('utf-8')) 287 | ssid_list = [] 288 | while True: 289 | ssid = ser.readline() 290 | if ssid == '\r\n': 291 | flag = True 292 | break 293 | ssid = ssid.strip('\r\n') 294 | ssid_list.append(ssid) 295 | 296 | if flag: 297 | thread.stop('') 298 | thread.join() 299 | else: 300 | thread.stop("\rsearch failure...\n") 301 | return None 302 | 303 | while 1: 304 | for x in range(len(ssid_list)): 305 | click.echo("%s.) %s" %(x, ssid_list[x])) 306 | click.secho('? ', fg='green', nl=False) 307 | value = click.prompt( 308 | click.style('Please select the network to which your Wio should connect', bold=True), 309 | type=int) 310 | if value >= 0 and value < len(ssid_list): 311 | ssid = ssid_list[value] 312 | break 313 | else: 314 | click.echo(click.style('>> ', fg='red') + "invalid input, range 0 to %s" %(len(ssid_list)-1)) 315 | 316 | ap = ssid 317 | else: 318 | ap = click.prompt(click.style('> ', fg='green') + 319 | click.style('Please enter the SSID of your Wi-Fi network', bold=True), type=str) 320 | 321 | ap_pwd = click.prompt(click.style('> ', fg='green') + 322 | click.style('Please enter your Wi-Fi network password (leave blank for none)', bold=True), 323 | default='', show_default=False) 324 | d_name = click.prompt(click.style('> ', fg='green') + 325 | click.style('Please enter the name of a device will be created', bold=True), type=str) 326 | 327 | click.echo(click.style('> ', fg='green') + "Here's what we're going to send to the Wio:") 328 | click.echo() 329 | click.echo(click.style('> ', fg='green') + "Wi-Fi network: " + 330 | click.style(ap, fg='green', bold=True)) 331 | ap_pwd_p = ap_pwd 332 | if ap_pwd_p == '': 333 | ap_pwd_p = 'None' 334 | click.echo(click.style('> ', fg='green') + "Password: " + 335 | click.style(ap_pwd_p, fg='green', bold=True)) 336 | click.echo(click.style('> ', fg='green') + "Device name: " + 337 | click.style(d_name, fg='green', bold=True)) 338 | click.echo() 339 | 340 | if click.confirm(click.style('? ', fg='green') + 341 | "Would you like to continue with the information shown above?", default=True): 342 | break 343 | 344 | click.echo() 345 | #waiting ui 346 | thread = termui.waiting_echo("Sending Wi-Fi information to device...") 347 | thread.daemon = True 348 | thread.start() 349 | 350 | # send serial command 351 | ## get version 352 | version = 1.1 353 | with serial.Serial(port, 115200, timeout=10) as ser: 354 | cmd = 'VERSION\r\n' 355 | ser.write(cmd.encode('utf-8')) 356 | res = ser.readline() 357 | try: 358 | version = float(re.match(r"([0-9]+.[0-9]+)", res).group(0)) 359 | except Exception as e: 360 | version = 1.1 361 | 362 | send_flag = False 363 | while 1: 364 | with serial.Serial(port, 115200, timeout=10) as ser: 365 | if version <= 1.1: 366 | cmd = "APCFG: %s\t%s\t%s\t%s\t%s\t%s\t\r\n" %(ap, ap_pwd, node_key, node_sn, xsvr_ip, msvr_ip) 367 | elif version >= 1.2: 368 | cmd = "APCFG: %s\t%s\t%s\t%s\t%s\t%s\t\r\n" %(ap, ap_pwd, node_key, node_sn, xsvr, msvr) 369 | else: 370 | cmd = "APCFG: %s\t%s\t%s\t%s\t%s\t%s\t\r\n" %(ap, ap_pwd, node_key, node_sn, xsvr, msvr) 371 | # click.echo(cmd) 372 | ser.write(cmd.encode('utf-8')) 373 | if "ok" in ser.readline(): 374 | click.echo(click.style('\r> ', fg='green') + "Send Wi-Fi information to device success.") 375 | thread.stop('') 376 | thread.join() 377 | send_flag = True 378 | if send_flag: 379 | break 380 | 381 | if send_flag: 382 | return {'name': d_name} 383 | else: 384 | return None 385 | 386 | @click.command() 387 | @pass_wio 388 | def cli(wio): 389 | ''' 390 | Add a new device with USB connect. 391 | 392 | \b 393 | DOES: 394 | Guides you through setting up a new device, and getting it on your network. 395 | 396 | \b 397 | USE: 398 | wio setup 399 | ''' 400 | token = wio.config.get("token", None) 401 | mserver_url = wio.config.get("mserver", None) 402 | msvr_ip = wio.config.get("mserver_ip", None) 403 | if not mserver_url or not token: 404 | click.echo(click.style('>> ', fg='red') + "Please login, use " + 405 | click.style("wio login", fg='green')) 406 | return 407 | msvr = urlparse(mserver_url).hostname 408 | xsvr = msvr 409 | xsvr_ip = msvr_ip 410 | board = '' 411 | 412 | click.secho('> ', fg='green', nl=False) 413 | click.echo("Setup is easy! Let's get started...\n") 414 | click.secho('! ', fg='green', nl=False) 415 | click.echo("PROTIP: " + 416 | click.style("Hold the ", fg='white') + 417 | click.style("Configure ", fg='green') + 418 | click.style("button ~4s into Configure Mode!", fg='white')) 419 | click.secho('! ', fg='green', nl=False) 420 | click.echo("PROTIP: " + 421 | click.style("Please make sure you are ", fg='white') + 422 | click.style("connected ", fg='green') + 423 | click.style("to the ", fg='white') + 424 | click.style("Server", fg='green')) 425 | click.echo() 426 | click.secho('? ', fg='green', nl=False) 427 | if not click.confirm(click.style('Would you like continue?', bold=True), default=True): 428 | click.echo('Quit setup!') 429 | return 430 | 431 | ### choice board 432 | while 1: 433 | for x in range(len(boards)): 434 | click.echo("%s.) %s" %(x, boards[x])) 435 | click.secho('? ', fg='green', nl=False) 436 | value = click.prompt(click.style('Please choice the board type', bold=True), type=int) 437 | if value >= 0 and value < len(boards): 438 | board = boards[value] 439 | break 440 | else: 441 | click.echo(click.style('>> ', fg='red') + "invalid input.") 442 | 443 | r = get_new(mserver_url, token, board) 444 | if not r: 445 | return 446 | node_key = r["node_key"] 447 | node_sn = r["node_sn"] 448 | 449 | ### list wio with serial 450 | if board == WIO_LINK_V1_0: 451 | try: 452 | ports = serial_list.serial_ports() 453 | except serial.SerialException as e: 454 | click.secho('>> ', fg='red', nl=False) 455 | click.echo(e) 456 | if e.errno == 13: 457 | click.echo("For more information, see https://github.com/Seeed-Studio/wio-cli#serial-port-permissions") 458 | return 459 | # click.echo(ports) 460 | count = len(ports) 461 | port = None 462 | if count == 0: 463 | pass #scan 464 | elif count == 1: 465 | port = ports[0] 466 | elif count >= 2: 467 | while 1: 468 | for x in range(len(ports)): 469 | click.echo("%s.) %s" %(x, ports[x])) 470 | click.secho('? ', fg='green', nl=False) 471 | value = click.prompt(click.style('Please choice a device', bold=True), type=int) 472 | if value >= 0 and value < len(ports): 473 | port = ports[value] 474 | break 475 | else: 476 | click.echo(click.style('>> ', fg='red') + "invalid input.") 477 | 478 | if not port: 479 | click.secho('> ', fg='green', nl=False) 480 | click.echo("No devices detected via USB.") 481 | # click.echo('>> change your network to wio_?') 482 | click.secho('? ', fg='green', nl=False) 483 | value = click.confirm( 484 | click.style('Would you like to enter Wi-Fi setup mode?', bold=True), default=True) 485 | 486 | #### udp setup 487 | r = upd_send(msvr, msvr_ip, xsvr, xsvr_ip, node_sn, node_key) 488 | if not r: 489 | return 490 | d_name = r['name'] 491 | check_connect(mserver_url, token, node_sn, d_name) 492 | return 493 | 494 | click.echo(click.style('> ', fg='green') + "I have detected a " + 495 | click.style("Wio ", fg='green') + "connected via USB.") 496 | 497 | r = serial_send(msvr, msvr_ip, xsvr, xsvr_ip, node_sn, node_key, port) 498 | if not r: 499 | return 500 | d_name = r['name'] 501 | check_connect(mserver_url, token, node_sn, d_name) 502 | elif board == WIO_NODE_V1_0: 503 | r = upd_send(msvr, msvr_ip, xsvr, xsvr_ip, node_sn, node_key) 504 | if not r: 505 | return 506 | d_name = r['name'] 507 | check_connect(mserver_url, token, node_sn, d_name) 508 | -------------------------------------------------------------------------------- /wio/commands/cmd_state.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio.wio import pass_wio 3 | 4 | @click.command() 5 | @pass_wio 6 | def cli(wio): 7 | ''' 8 | Login state. 9 | 10 | \b 11 | DOES: 12 | Display login email, token, server url. 13 | 14 | \b 15 | USE: 16 | wio state 17 | ''' 18 | user_token = wio.config.get("token", None) 19 | mserver_url = wio.config.get("mserver", None) 20 | if not mserver_url or not user_token: 21 | click.echo(click.style('>> ', fg='red') + "Please login, use " + 22 | click.style("wio login", fg='green')) 23 | return 24 | 25 | email = wio.config.get("email",None) 26 | server = wio.config.get("server",None) 27 | token = wio.config.get("token",None) 28 | click.secho('> ', fg='green', nl=False) 29 | click.echo("server: " + click.style(server, fg='green', bold=True) + ', ' + 30 | click.style(mserver_url, fg='green', bold=True)) 31 | click.secho('> ', fg='green', nl=False) 32 | click.echo("email: " + click.style(email, fg='green', bold=True)) 33 | click.secho('> ', fg='green', nl=False) 34 | click.echo("token: " + click.style(token, fg='green', bold=True)) 35 | -------------------------------------------------------------------------------- /wio/commands/cmd_udp.py: -------------------------------------------------------------------------------- 1 | import click 2 | from wio.wio import pass_wio 3 | from wio import udp 4 | 5 | 6 | @click.command() 7 | @click.option('--send', nargs=1, type=unicode, help="Sends a UDP command to the wio device") 8 | @pass_wio 9 | def cli(wio, send): 10 | ''' 11 | Sends a UDP command to the wio device. 12 | 13 | \b 14 | DOES: 15 | Support "VERSION", "SCAN", "Blank?", "DEBUG", "ENDEBUG: 1", "ENDEBUG: 0" 16 | "APCFG: AP\\tPWDs\\tTOKENs\\tSNs\\tSERVER_Domains\\tXSERVER_Domain\\t\\r\\n", 17 | Note: 18 | 1. Ensure your device is Configure Mode. 19 | 2. Change your computer network to Wio's AP. 20 | 21 | \b 22 | EXAMPLE: 23 | wio udp --send [command], send UPD command 24 | ''' 25 | command = send 26 | click.echo("UDP command: {}".format(command)) 27 | result = udp.common_send(command) 28 | if result is None: 29 | return debug_error() 30 | else: 31 | click.echo(result) 32 | 33 | def debug_error(): 34 | click.echo("UDP command work failure!!") 35 | error_tip() 36 | 37 | def error_tip(): 38 | click.echo("Note:") 39 | click.echo(" 1. Ensure your device is Configure Mode.") 40 | click.echo(" 2. Change your computer network to Wio's AP.") 41 | -------------------------------------------------------------------------------- /wio/config_default.py: -------------------------------------------------------------------------------- 1 | # Seeed account login for third-party app 2 | SEEED_APPID = "" 3 | SEEED_APPKEY = "" 4 | SEEED_COMMON = "" 5 | 6 | # Wio Paras for exchange user 7 | WIO_REGION = "" 8 | WIO_SECKET = "" 9 | -------------------------------------------------------------------------------- /wio/serial_list.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import glob 3 | import serial 4 | 5 | 6 | def serial_ports(): 7 | """ Lists serial port names 8 | 9 | :raises EnvironmentError: 10 | On unsupported or unknown platforms 11 | :returns: 12 | A list of the serial ports available on the system 13 | """ 14 | if sys.platform.startswith('win'): 15 | ports = ['COM%s' % (i + 1) for i in range(256)] 16 | elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'): 17 | # this excludes your current terminal "/dev/tty" 18 | ports = glob.glob('/dev/ttyUSB*') # ubuntu is /dev/ttyUSB0 19 | elif sys.platform.startswith('darwin'): 20 | # ports = glob.glob('/dev/tty.*') 21 | ports = glob.glob('/dev/tty.SLAB_USBtoUART*') 22 | else: 23 | raise EnvironmentError('Unsupported platform') 24 | 25 | result = [] 26 | for port in ports: 27 | try: 28 | s = serial.Serial(port) 29 | s.close() 30 | result.append(port) 31 | except serial.SerialException as e: 32 | if e.errno == 13: 33 | raise e 34 | pass 35 | except OSError: 36 | pass 37 | return result 38 | 39 | 40 | # if __name__ == '__main__': 41 | # print(serial_ports()) 42 | -------------------------------------------------------------------------------- /wio/termui.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | import click 3 | import threading 4 | 5 | class waiting_echo(threading.Thread): 6 | def __init__(self, msg): 7 | threading.Thread.__init__(self) 8 | self.msg = msg 9 | self.exiting=False 10 | self.flag = True 11 | def run(self): 12 | while not self.exiting: 13 | click.echo("\r-%s" %self.msg, nl=False) 14 | click.echo(" "*(80-len(self.msg)), nl=False) 15 | click.echo("\b"*(80-len(self.msg)), nl=False) 16 | sleep(0.1) 17 | click.echo("\r\%s" %self.msg, nl=False) 18 | click.echo(" "*(80-len(self.msg)), nl=False) 19 | click.echo("\b"*(80-len(self.msg)), nl=False) 20 | sleep(0.1) 21 | click.echo("\r|%s" %self.msg, nl=False) 22 | click.echo(" "*(80-len(self.msg)), nl=False) 23 | click.echo("\b"*(80-len(self.msg)), nl=False) 24 | sleep(0.1) 25 | click.echo("\r/%s" %self.msg, nl=False) 26 | click.echo(" "*(80-len(self.msg)), nl=False) 27 | click.echo("\b"*(80-len(self.msg)), nl=False) 28 | sleep(0.1) 29 | 30 | click.echo('\r' + " "*(80-len(self.msg)), nl=False) 31 | click.echo('\r', nl=False) 32 | def message(self, msg): 33 | self.msg = msg 34 | def stop(self, msg): 35 | self.exiting = True 36 | self.msg = msg 37 | 38 | def tree(list): 39 | if not list: 40 | click.echo('No Wio devices could be found.') 41 | return 42 | for l in list[:-1]: 43 | click.echo('|-- ', nl=False) 44 | if l['online']: 45 | click.secho(l['name'] + ' (%s) [%s]' %(l['onoff'], l['board'].split()[1]), fg='green') 46 | else: 47 | click.secho(l['name'] + ' (%s) [%s]' %(l['onoff'], l['board'].split()[1]), fg='cyan') 48 | click.echo('| |-- ', nl=False) 49 | click.echo('sn: ' + l['node_sn']) 50 | click.echo('| |-- ', nl=False) 51 | click.echo('token: ' + l['node_key']) 52 | click.echo('| |-- ', nl=False) 53 | click.echo('API url: ' + l['resources']) 54 | click.echo('| |-- ', nl=False) 55 | click.echo('APIs: ') 56 | for api in l['well_known']: 57 | click.echo('| |-- ', nl=False) 58 | click.echo(api) 59 | 60 | l = list[-1] 61 | click.echo('|-- ', nl=False) 62 | if l['online']: 63 | click.secho(l['name'] + ' (%s) [%s]' %(l['onoff'], l['board'].split()[1]), fg='green') 64 | else: 65 | click.secho(l['name'] + ' (%s) [%s]' %(l['onoff'], l['board'].split()[1]), fg='cyan') 66 | click.echo(' |-- ', nl=False) 67 | click.echo('sn: ' + l['node_sn']) 68 | click.echo(' |-- ', nl=False) 69 | click.echo('token: ' + l['node_key']) 70 | click.echo(' |-- ', nl=False) 71 | click.echo('API url: ' + l['resources']) 72 | click.echo(' |-- ', nl=False) 73 | click.echo('APIs: ') 74 | for api in l['well_known']: 75 | click.echo(' |-- ', nl=False) 76 | click.echo(api) 77 | -------------------------------------------------------------------------------- /wio/udp.py: -------------------------------------------------------------------------------- 1 | # Send UDP broadcast packets 2 | import socket 3 | import re 4 | 5 | PORT = 1025 6 | IP = "192.168.4.1" 7 | cmd = "SCAN" 8 | addr = (IP, PORT) 9 | 10 | def udp_list(): 11 | s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 12 | s.settimeout(1) 13 | ssid_list = [] 14 | flag = False 15 | for i in range(3): 16 | s.sendto('SCAN', addr) 17 | try: 18 | while 1: 19 | data, a = s.recvfrom(1024) 20 | ssid = data 21 | if ssid in '\r\n': 22 | flag = True 23 | break 24 | ssid = ssid.strip('\r\n') 25 | ssid_list.append(ssid) 26 | except socket.timeout: 27 | continue 28 | except: 29 | break 30 | 31 | if flag: 32 | break 33 | s.close() 34 | 35 | if flag: 36 | return ssid_list 37 | else: 38 | return None 39 | 40 | def udp_version(): 41 | s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 42 | s.settimeout(1) 43 | version = 1.1 44 | flag = False 45 | for i in range(3): 46 | s.sendto('VERSION', addr) 47 | try: 48 | while 1: 49 | data, a = s.recvfrom(1024) 50 | flag = True 51 | break 52 | except socket.timeout: 53 | continue 54 | except: 55 | break 56 | 57 | if flag: 58 | break 59 | s.close() 60 | 61 | if flag: 62 | try: 63 | version = float(re.match(r"([0-9]+.[0-9]+)", data).group(0)) 64 | except Exception as e: 65 | version = 1.1 66 | return version 67 | else: 68 | return version 69 | 70 | def udp_debug(): 71 | s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 72 | s.settimeout(1) 73 | flag = False 74 | for i in range(3): 75 | s.sendto('DEBUG', addr) 76 | try: 77 | while 1: 78 | data, a = s.recvfrom(1024) 79 | flag = True 80 | break 81 | except socket.timeout: 82 | continue 83 | except: 84 | break 85 | 86 | if flag: 87 | break 88 | s.close() 89 | 90 | if flag: 91 | try: 92 | debug = re.match(r"([0-9])", data).group(0) 93 | return debug 94 | except Exception as e: 95 | raise e 96 | 97 | return flag 98 | 99 | def send(cmd): 100 | s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 101 | s.settimeout(1) 102 | flag = False 103 | for i in range(3): 104 | try: 105 | s.sendto(cmd, addr) 106 | while 1: 107 | data, a = s.recvfrom(1024) 108 | if 'ok' in data: 109 | flag = True 110 | break 111 | except socket.timeout: 112 | continue 113 | except: 114 | break 115 | 116 | if flag: 117 | break 118 | s.close() 119 | 120 | return flag 121 | 122 | def common_send(cmd): 123 | s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 124 | s.settimeout(1) 125 | cmd = cmd.replace(r"\t", "\t") 126 | cmd = cmd.replace(r"\n", "\n") 127 | cmd = cmd.replace(r"\r", "\r") 128 | for i in range(5): 129 | # print "try: ", i 130 | result = "" 131 | flag = False 132 | try: 133 | s.sendto(cmd, addr) 134 | while 1: 135 | data, adr = s.recvfrom(1024) 136 | # print data.encode('string_escape') 137 | result += data 138 | if data == "\r\n" or data == "ok\r\n" or "192.168.4.1\r\n" in data: 139 | flag = True 140 | break 141 | except socket.timeout: 142 | continue 143 | except Exception as e: 144 | print e 145 | break 146 | if flag: 147 | break 148 | s.close() 149 | 150 | return result if flag else None 151 | -------------------------------------------------------------------------------- /wio/wio.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import posixpath 4 | import json 5 | import socket 6 | from . import termui 7 | import click 8 | import requests 9 | import signal 10 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 11 | try: 12 | from urllib.parse import urlparse 13 | except ImportError: 14 | from urlparse import urlparse 15 | 16 | 17 | version = '0.3.2' 18 | 19 | WIO_LINK_V1_0 = 'Wio Link v1.0' 20 | WIO_NODE_V1_0 = 'Wio Node v1.0' 21 | boards = [WIO_LINK_V1_0, WIO_NODE_V1_0] 22 | 23 | login_endpoint = "/v1/user/login" 24 | ext_user_endpoint = "/v1/ext_users" 25 | node_list_endpoint = "/v1/nodes/list" 26 | well_known_endpoint = "/v1/node/.well-known" 27 | nodes_create_endpoint = "/v1/nodes/create" 28 | nodes_rename_endpoint = "/v1/nodes/rename" 29 | nodes_delete_endpoint = "/v1/nodes/delete" 30 | node_resources_endpoint = "/v1/node/resources" 31 | 32 | verify = False 33 | 34 | CONTEXT_SETTINGS = dict(auto_envvar_prefix='WIO') 35 | 36 | class Wio(object): 37 | 38 | def __init__(self): 39 | # self.home = home 40 | self.config = {} 41 | # self.verbose = False 42 | 43 | def set_config(self, key, value): 44 | self.config[key] = value 45 | # cur_dir = os.path.split(os.path.realpath(__file__))[0] 46 | cur_dir = os.path.abspath(os.path.expanduser("~/.wio")) 47 | db_file_path = '%s/config.json' % cur_dir 48 | open("%s/config.json"%cur_dir,"w").write(json.dumps(self.config)) 49 | # if self.verbose: 50 | # click.echo('config[%s] = %s' % (key, value), file=sys.stderr) 51 | 52 | 53 | pass_wio = click.make_pass_decorator(Wio, ensure=True) 54 | cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__),'commands')) 55 | 56 | 57 | class ComplexCLI(click.MultiCommand): 58 | 59 | def list_commands(self, ctx): 60 | rv = [] 61 | for filename in os.listdir(cmd_folder): 62 | if filename.endswith('.py') and \ 63 | filename.startswith('cmd_'): 64 | rv.append(filename[4:-3]) 65 | rv.sort() 66 | return rv 67 | 68 | def get_command(self, ctx, name): 69 | try: 70 | if sys.version_info[0] == 2: 71 | name = name.encode('ascii', 'replace') 72 | mod = __import__('wio.commands.cmd_' + name, 73 | None, None, ['cli']) 74 | except ImportError: 75 | return 76 | return mod.cli 77 | 78 | 79 | def sigint_handler(signum, frame): 80 | click.echo() 81 | exit(0) 82 | 83 | @click.command(cls=ComplexCLI, context_settings=CONTEXT_SETTINGS) 84 | @click.version_option(version) 85 | @click.pass_context 86 | def cli(ctx): 87 | """\b 88 | Welcome to the Wio Command line utility! 89 | https://github.com/Seeed-Studio/wio-cli 90 | 91 | For more information Run: wio --help 92 | """ 93 | ctx.obj = Wio() 94 | cur_dir = os.path.abspath(os.path.expanduser("~/.wio")) 95 | if not os.path.exists(cur_dir): 96 | text = {"email":"", "token":""} 97 | os.mkdir(cur_dir) 98 | open("%s/config.json"%cur_dir,"w").write(json.dumps(text)) 99 | db_file_path = '%s/config.json' % cur_dir 100 | config = json.load(open(db_file_path)) 101 | ctx.obj.config = config 102 | 103 | signal.signal(signal.SIGINT, sigint_handler) 104 | 105 | if not verify: 106 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 107 | 108 | def choise_server(wio): 109 | while True: 110 | click.echo("1.) Global Server (New)[https://us.wio.seeed.io]") 111 | # click.echo("2.) Global Server (Old)[https://iot.seeed.cc]") 112 | click.echo("2.) Chinese Server [https://cn.wio.seeed.io]") 113 | click.echo("3.) Customize Server") 114 | click.secho('? ', fg='green', nl=False) 115 | server = click.prompt(click.style('Please choice server', bold=True), type=int) 116 | if server == 1: 117 | wio.set_config("mserver","https://us.wio.seeed.io") 118 | wio.set_config("server","Global") 119 | wio.set_config("mserver_ip","54.186.73.152") 120 | return 121 | # elif server == 2: 122 | # wio.set_config("mserver","https://iot.seeed.cc") 123 | # wio.set_config("server","Global") 124 | # wio.set_config("mserver_ip","45.79.4.239") 125 | # return 126 | elif server == 2: 127 | wio.set_config("mserver","https://cn.wio.seeed.io") 128 | wio.set_config("server","Chinese") 129 | wio.set_config("mserver_ip","120.25.216.117") 130 | return 131 | elif server == 3: 132 | wio.set_config("server","Customize") 133 | break 134 | else: 135 | click.echo(click.style('>> ', fg='red') + "invalid input.") 136 | continue 137 | 138 | while 1: 139 | click.secho('? ', fg='green', nl=False) 140 | mserver = click.prompt(click.style("Please enter Customize Server url\n(e.g. https://us.wio.seeed.io or http://192.168.1.10:8080)", bold=True)) 141 | try: 142 | hostname = urlparse(mserver).hostname 143 | mserver_ip = socket.gethostbyname(hostname) 144 | break 145 | except (IndexError, TypeError): 146 | click.secho(">> url is not correct format!", fg='red') 147 | continue 148 | except Exception as e: 149 | click.secho(">> %s" %e, fg='red') 150 | continue 151 | 152 | wio.set_config("mserver", mserver) 153 | wio.set_config("mserver_ip", mserver_ip) 154 | --------------------------------------------------------------------------------