Skip to content

Commit

Permalink
Extend "pio device list" command with new options to list logical dev…
Browse files Browse the repository at this point in the history
…ices and multicast DNS services // Resolve #463
  • Loading branch information
ivankravets committed Dec 18, 2017
1 parent 724135f commit 31814b5
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 40 deletions.
16 changes: 12 additions & 4 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@ PlatformIO 3.0
* Allowed to depend on development platform using VSC URL (Git, Mercurial and Subversion)
in `Project Configuration File "platformio.ini" <http://docs.platformio.org/en/latest/projectconf/section_env_general.html#platform>`__
Dropped support for ``*_stage`` dev/platforms. Use VCS URL instead.
* Improvements to `Library Dependency Finder (LDF) <http://docs.platformio.org/page/librarymanager/ldf.html>`__:
* New options for `platformio device list <http://docs.platformio.org/en/latest/userguide/cmd_device.html#platformio-device-list>`__
command:

- Search for libraries used in test
- ``--serial`` list available serial ports (default)
- ``--logical`` list logical devices
- ``--mdns`` discover multicast DNS services
(`issue #463 <https://github.com/platformio/platformio-core/issues/463>`_)

* `Library Dependency Finder (LDF) <http://docs.platformio.org/page/librarymanager/ldf.html>`__:

- Search for dependencies used in `PIO Unit Testing <http://docs.platformio.org/page/plus/unit-testing.html>`__
(`issue #953 <https://github.com/platformio/platformio-core/issues/953>`_)
- Parse library source file in pair with a header when they have the same name
(`issue #1175 <https://github.com/platformio/platformio-core/issues/1175>`_)
- Handle library dependencies defined as VCS or SemVer in
`Project Configuration File "platformio.ini" <http://docs.platformio.org/en/latest/projectconf/section_env_general.html#platform>`__
`Project Configuration File "platformio.ini" <http://docs.platformio.org/page/projectconf/section_env_general.html#platform>`__
(`issue #1155 <https://github.com/platformio/platformio-core/issues/1155>`_)
- Added option to configure library `Compatible Mode <http://docs.platformio.org/en/latest/librarymanager/ldf.html#compatibility-mode>`__
- Added option to configure library `Compatible Mode <http://docs.platformio.org/page/librarymanager/ldf.html#compatibility-mode>`__
using `library.json <http://docs.platformio.org/page/librarymanager/config.html>`__

* Fixed platforms, packages, and libraries updating behind proxy
Expand Down
2 changes: 1 addition & 1 deletion docs
Submodule docs updated 1 files
+70 −2 userguide/cmd_device.rst
16 changes: 8 additions & 8 deletions platformio/builder/tools/pioupload.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def WaitForNewSerialPort(env, before):
elapsed = 0
before = [p['port'] for p in before]
while elapsed < 5 and new_port is None:
now = [p['port'] for p in util.get_serialports()]
now = [p['port'] for p in util.get_serial_ports()]
for p in now:
if p not in before:
new_port = p
Expand Down Expand Up @@ -107,18 +107,18 @@ def _is_match_pattern(port):

def _look_for_mbed_disk():
msdlabels = ("mbed", "nucleo", "frdm", "microbit")
for item in util.get_logicaldisks():
if item['disk'].startswith("/net") or not _is_match_pattern(
item['disk']):
for item in util.get_logical_devices():
if item['device'].startswith("/net") or not _is_match_pattern(
item['device']):
continue
mbed_pages = [
join(item['disk'], n) for n in ("mbed.htm", "mbed.html")
join(item['device'], n) for n in ("mbed.htm", "mbed.html")
]
if any([isfile(p) for p in mbed_pages]):
return item['disk']
return item['device']
if item['name'] \
and any([l in item['name'].lower() for l in msdlabels]):
return item['disk']
return item['device']
return None

def _look_for_serial_port():
Expand All @@ -127,7 +127,7 @@ def _look_for_serial_port():
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
if "BOARD" in env and "build.hwids" in env.BoardConfig():
board_hwids = env.BoardConfig().get("build.hwids")
for item in util.get_serialports(filter_hwid=True):
for item in util.get_serial_ports(filter_hwid=True):
if not _is_match_pattern(item['port']):
continue
port = item['port']
Expand Down
72 changes: 62 additions & 10 deletions platformio/commands/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,69 @@ def cli():


@cli.command("list", short_help="List devices")
@click.option("--serial", is_flag=True, help="List serial ports, default")
@click.option("--logical", is_flag=True, help="List logical devices")
@click.option("--mdns", is_flag=True, help="List multicast DNS services")
@click.option("--json-output", is_flag=True)
def device_list(json_output):
if json_output:
return click.echo(json.dumps(util.get_serialports()))
def device_list( # pylint: disable=too-many-branches
serial, logical, mdns, json_output):
if not logical and not mdns:
serial = True
data = {}
if serial:
data['serial'] = util.get_serial_ports()
if logical:
data['logical'] = util.get_logical_devices()
if mdns:
data['mdns'] = util.get_mdns_services()

single_key = data.keys()[0] if len(data.keys()) == 1 else None

for item in util.get_serialports():
click.secho(item['port'], fg="cyan")
click.echo("-" * len(item['port']))
click.echo("Hardware ID: %s" % item['hwid'])
click.echo("Description: %s" % item['description'])
click.echo("")
if json_output:
return click.echo(json.dumps(data[single_key] if single_key else data))

titles = {
"serial": "Serial Ports",
"logical": "Logical Devices",
"mdns": "Multicast DNS Services"
}

for key, value in data.iteritems():
if not single_key:
click.secho(titles[key], bold=True)
click.echo("=" * len(titles[key]))

if key == "serial":
for item in value:
click.secho(item['port'], fg="cyan")
click.echo("-" * len(item['port']))
click.echo("Hardware ID: %s" % item['hwid'])
click.echo("Description: %s" % item['description'])
click.echo("")

if key == "logical":
for item in value:
click.secho(item['device'], fg="cyan")
click.echo("-" * len(item['device']))
click.echo("Name: %s" % item['name'])
click.echo("")

if key == "mdns":
for item in value:
click.secho(item['name'], fg="cyan")
click.echo("-" * len(item['name']))
click.echo("Type: %s" % item['type'])
click.echo("IP: %s" % item['ip'])
click.echo("Port: %s" % item['port'])
if item['properties']:
click.echo("Properties: %s" % ("; ".join([
"%s=%s" % (k, v)
for k, v in item['properties'].iteritems()
])))
click.echo("")

if single_key:
click.echo("")

return True

Expand Down Expand Up @@ -123,7 +175,7 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
pass

if not kwargs['port']:
ports = util.get_serialports(filter_hwid=True)
ports = util.get_serial_ports(filter_hwid=True)
if len(ports) == 1:
kwargs['port'] = ports[0]['port']

Expand Down
97 changes: 81 additions & 16 deletions platformio/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import click
import requests
import zeroconf

from platformio import __apiurl__, __version__, exception

Expand Down Expand Up @@ -417,7 +418,7 @@ def copy_pythonpath_to_osenv():
os.environ['PYTHONPATH'] = os.pathsep.join(_PYTHONPATH)


def get_serialports(filter_hwid=False):
def get_serial_ports(filter_hwid=False):
try:
from serial.tools.list_ports import comports
except ImportError:
Expand Down Expand Up @@ -445,42 +446,106 @@ def get_serialports(filter_hwid=False):
return result


def get_logicaldisks():
disks = []
def get_logical_devices():
items = []
if platform.system() == "Windows":
try:
result = exec_command(
["wmic", "logicaldisk", "get", "name,VolumeName"]).get(
"out", "")
disknamere = re.compile(r"^([A-Z]{1}\:)\s*(\S+)?")
devicenamere = re.compile(r"^([A-Z]{1}\:)\s*(\S+)?")
for line in result.split("\n"):
match = disknamere.match(line.strip())
match = devicenamere.match(line.strip())
if not match:
continue
disks.append({
"disk": match.group(1) + "\\",
items.append({
"device": match.group(1) + "\\",
"name": match.group(2)
})
return disks
return items
except WindowsError: # pylint: disable=undefined-variable
pass
# try "fsutil"
result = exec_command(["fsutil", "fsinfo", "drives"]).get("out", "")
for disk in re.findall(r"[A-Z]:\\", result):
disks.append({"disk": disk, "name": None})
return disks
for device in re.findall(r"[A-Z]:\\", result):
items.append({"device": device, "name": None})
return items
else:
result = exec_command(["df"]).get("out")
disknamere = re.compile(r"^/.+\d+\%\s+([a-z\d\-_/]+)$", flags=re.I)
devicenamere = re.compile(r"^/.+\d+\%\s+([a-z\d\-_/]+)$", flags=re.I)
for line in result.split("\n"):
match = disknamere.match(line.strip())
match = devicenamere.match(line.strip())
if not match:
continue
disks.append({
"disk": match.group(1),
items.append({
"device": match.group(1),
"name": basename(match.group(1))
})
return disks
return items


### Backward compatibility for PIO Core <3.5
get_serialports = get_serial_ports
get_logicaldisks = lambda: [{
"disk": d['device'],
"name": d['name']
} for d in get_logical_devices()]


def get_mdns_services():

class mDNSListener(object):

def __init__(self):
self._zc = zeroconf.Zeroconf(
interfaces=zeroconf.InterfaceChoice.All)
self._found_types = []
self._found_services = []

def __enter__(self):
zeroconf.ServiceBrowser(self._zc, "_services._dns-sd._udp.local.",
self)
return self

def __exit__(self, etype, value, traceback):
self._zc.close()

def remove_service(self, zc, type_, name):
pass

def add_service(self, zc, type_, name):
try:
zeroconf.service_type_name(name)
except zeroconf.BadTypeInNameException:
return
if name not in self._found_types:
self._found_types.append(name)
zeroconf.ServiceBrowser(self._zc, name, self)
if type_ in self._found_types:
s = zc.get_service_info(type_, name)
if s:
self._found_services.append(s)

def get_services(self):
return self._found_services

items = []
with mDNSListener() as mdns:
sleep(5)
for service in mdns.get_services():
items.append({
"type":
service.type,
"name":
service.name,
"ip":
".".join([str(ord(c)) for c in service.address]),
"port":
service.port,
"properties":
service.properties
})
return items


def get_request_defheaders():
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"lockfile>=0.9.1,<0.13",
"pyserial>=3,<4,!=3.3",
"requests>=2.4.0,<3",
"semantic_version>=2.5.0,<3"
"semantic_version>=2.5.0,<3",
"zeroconf<=0.19.1"
]

setup(
Expand Down

0 comments on commit 31814b5

Please sign in to comment.