forked from platformio/platformio-core
Extend "pio device list" command with new options to list logical devices and multicast DNS services // Resolve #463
This commit is contained in:
16
HISTORY.rst
16
HISTORY.rst
@ -15,16 +15,24 @@ PlatformIO 3.0
|
|||||||
* Allowed to depend on development platform using VSC URL (Git, Mercurial and Subversion)
|
* 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>`__
|
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.
|
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>`_)
|
(`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
|
- 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>`_)
|
(`issue #1175 <https://github.com/platformio/platformio-core/issues/1175>`_)
|
||||||
- Handle library dependencies defined as VCS or SemVer in
|
- 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>`_)
|
(`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>`__
|
using `library.json <http://docs.platformio.org/page/librarymanager/config.html>`__
|
||||||
|
|
||||||
* Fixed platforms, packages, and libraries updating behind proxy
|
* Fixed platforms, packages, and libraries updating behind proxy
|
||||||
|
2
docs
2
docs
Submodule docs updated: a9a8554ed0...0c984c49f2
@ -58,7 +58,7 @@ def WaitForNewSerialPort(env, before):
|
|||||||
elapsed = 0
|
elapsed = 0
|
||||||
before = [p['port'] for p in before]
|
before = [p['port'] for p in before]
|
||||||
while elapsed < 5 and new_port is None:
|
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:
|
for p in now:
|
||||||
if p not in before:
|
if p not in before:
|
||||||
new_port = p
|
new_port = p
|
||||||
@ -107,18 +107,18 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
|
|||||||
|
|
||||||
def _look_for_mbed_disk():
|
def _look_for_mbed_disk():
|
||||||
msdlabels = ("mbed", "nucleo", "frdm", "microbit")
|
msdlabels = ("mbed", "nucleo", "frdm", "microbit")
|
||||||
for item in util.get_logicaldisks():
|
for item in util.get_logical_devices():
|
||||||
if item['disk'].startswith("/net") or not _is_match_pattern(
|
if item['device'].startswith("/net") or not _is_match_pattern(
|
||||||
item['disk']):
|
item['device']):
|
||||||
continue
|
continue
|
||||||
mbed_pages = [
|
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]):
|
if any([isfile(p) for p in mbed_pages]):
|
||||||
return item['disk']
|
return item['device']
|
||||||
if item['name'] \
|
if item['name'] \
|
||||||
and any([l in item['name'].lower() for l in msdlabels]):
|
and any([l in item['name'].lower() for l in msdlabels]):
|
||||||
return item['disk']
|
return item['device']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _look_for_serial_port():
|
def _look_for_serial_port():
|
||||||
@ -127,7 +127,7 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
|
|||||||
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
|
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
|
||||||
if "BOARD" in env and "build.hwids" in env.BoardConfig():
|
if "BOARD" in env and "build.hwids" in env.BoardConfig():
|
||||||
board_hwids = env.BoardConfig().get("build.hwids")
|
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']):
|
if not _is_match_pattern(item['port']):
|
||||||
continue
|
continue
|
||||||
port = item['port']
|
port = item['port']
|
||||||
|
@ -28,17 +28,69 @@ def cli():
|
|||||||
|
|
||||||
|
|
||||||
@cli.command("list", short_help="List devices")
|
@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)
|
@click.option("--json-output", is_flag=True)
|
||||||
def device_list(json_output):
|
def device_list( # pylint: disable=too-many-branches
|
||||||
if json_output:
|
serial, logical, mdns, json_output):
|
||||||
return click.echo(json.dumps(util.get_serialports()))
|
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()
|
||||||
|
|
||||||
for item in util.get_serialports():
|
single_key = data.keys()[0] if len(data.keys()) == 1 else None
|
||||||
click.secho(item['port'], fg="cyan")
|
|
||||||
click.echo("-" * len(item['port']))
|
if json_output:
|
||||||
click.echo("Hardware ID: %s" % item['hwid'])
|
return click.echo(json.dumps(data[single_key] if single_key else data))
|
||||||
click.echo("Description: %s" % item['description'])
|
|
||||||
click.echo("")
|
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
|
return True
|
||||||
|
|
||||||
@ -123,7 +175,7 @@ def device_monitor(**kwargs): # pylint: disable=too-many-branches
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if not kwargs['port']:
|
if not kwargs['port']:
|
||||||
ports = util.get_serialports(filter_hwid=True)
|
ports = util.get_serial_ports(filter_hwid=True)
|
||||||
if len(ports) == 1:
|
if len(ports) == 1:
|
||||||
kwargs['port'] = ports[0]['port']
|
kwargs['port'] = ports[0]['port']
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ from time import sleep, time
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
import requests
|
import requests
|
||||||
|
import zeroconf
|
||||||
|
|
||||||
from platformio import __apiurl__, __version__, exception
|
from platformio import __apiurl__, __version__, exception
|
||||||
|
|
||||||
@ -417,7 +418,7 @@ def copy_pythonpath_to_osenv():
|
|||||||
os.environ['PYTHONPATH'] = os.pathsep.join(_PYTHONPATH)
|
os.environ['PYTHONPATH'] = os.pathsep.join(_PYTHONPATH)
|
||||||
|
|
||||||
|
|
||||||
def get_serialports(filter_hwid=False):
|
def get_serial_ports(filter_hwid=False):
|
||||||
try:
|
try:
|
||||||
from serial.tools.list_ports import comports
|
from serial.tools.list_ports import comports
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -445,42 +446,106 @@ def get_serialports(filter_hwid=False):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_logicaldisks():
|
def get_logical_devices():
|
||||||
disks = []
|
items = []
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
try:
|
try:
|
||||||
result = exec_command(
|
result = exec_command(
|
||||||
["wmic", "logicaldisk", "get", "name,VolumeName"]).get(
|
["wmic", "logicaldisk", "get", "name,VolumeName"]).get(
|
||||||
"out", "")
|
"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"):
|
for line in result.split("\n"):
|
||||||
match = disknamere.match(line.strip())
|
match = devicenamere.match(line.strip())
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
disks.append({
|
items.append({
|
||||||
"disk": match.group(1) + "\\",
|
"device": match.group(1) + "\\",
|
||||||
"name": match.group(2)
|
"name": match.group(2)
|
||||||
})
|
})
|
||||||
return disks
|
return items
|
||||||
except WindowsError: # pylint: disable=undefined-variable
|
except WindowsError: # pylint: disable=undefined-variable
|
||||||
pass
|
pass
|
||||||
# try "fsutil"
|
# try "fsutil"
|
||||||
result = exec_command(["fsutil", "fsinfo", "drives"]).get("out", "")
|
result = exec_command(["fsutil", "fsinfo", "drives"]).get("out", "")
|
||||||
for disk in re.findall(r"[A-Z]:\\", result):
|
for device in re.findall(r"[A-Z]:\\", result):
|
||||||
disks.append({"disk": disk, "name": None})
|
items.append({"device": device, "name": None})
|
||||||
return disks
|
return items
|
||||||
else:
|
else:
|
||||||
result = exec_command(["df"]).get("out")
|
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"):
|
for line in result.split("\n"):
|
||||||
match = disknamere.match(line.strip())
|
match = devicenamere.match(line.strip())
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
disks.append({
|
items.append({
|
||||||
"disk": match.group(1),
|
"device": match.group(1),
|
||||||
"name": basename(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():
|
def get_request_defheaders():
|
||||||
|
Reference in New Issue
Block a user