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)
|
||||
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
|
||||
|
2
docs
2
docs
Submodule docs updated: a9a8554ed0...0c984c49f2
@ -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
|
||||
@ -107,18 +107,18 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
|
||||
|
||||
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():
|
||||
@ -127,7 +127,7 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
|
||||
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']
|
||||
|
@ -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()
|
||||
|
||||
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("")
|
||||
single_key = data.keys()[0] if len(data.keys()) == 1 else None
|
||||
|
||||
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
|
||||
|
||||
@ -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']
|
||||
|
||||
|
@ -32,6 +32,7 @@ from time import sleep, time
|
||||
|
||||
import click
|
||||
import requests
|
||||
import zeroconf
|
||||
|
||||
from platformio import __apiurl__, __version__, exception
|
||||
|
||||
@ -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:
|
||||
@ -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():
|
||||
|
Reference in New Issue
Block a user