summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Undheim <ruben.undheim@gmail.com>2019-01-29 14:58:17 +0000
committerRuben Undheim <ruben.undheim@gmail.com>2019-01-29 14:58:17 +0000
commit709e6ab3bb2aed51f318a9c06a399ae6e75f5c71 (patch)
treee31258a2a8d5efc24973c2a39abef024cf97cf38
parentcaf9fef15cccb764435dfd99d6100f600ff0943b (diff)
parent42202e038d291ed2e78886ab99c77e859a822f28 (diff)
Update upstream source from tag 'upstream/2.3.0'
Update to upstream version '2.3.0' with Debian dir e8dfaf20cadb97960fdc2ce05dc1e9a0c9ef6a79
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml2
-rw-r--r--README.md2
-rw-r--r--netdisco/daikin.py9
-rw-r--r--netdisco/discoverables/__init__.py6
-rw-r--r--netdisco/discoverables/esphome.py9
-rw-r--r--netdisco/discoverables/openhome.py3
-rw-r--r--netdisco/discoverables/wink.py5
-rw-r--r--netdisco/discovery.py3
-rw-r--r--netdisco/gdm.py11
-rw-r--r--netdisco/lms.py6
-rw-r--r--netdisco/mdns.py6
-rw-r--r--netdisco/service.py5
-rw-r--r--netdisco/smartglass.py5
-rw-r--r--netdisco/ssdp.py7
-rw-r--r--netdisco/tellstick.py7
-rw-r--r--netdisco/util.py14
-rw-r--r--requirements.txt1
-rw-r--r--setup.cfg16
-rw-r--r--setup.py44
-rw-r--r--tox.ini8
21 files changed, 125 insertions, 45 deletions
diff --git a/.gitignore b/.gitignore
index edc491c..a496cdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ lib64
pip-selfcheck.json
.tox
.pytest_cache/
+.mypy_cache/
diff --git a/.travis.yml b/.travis.yml
index ac9af0a..80155d3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,8 @@ matrix:
env: TOXENV=py34
- python: "3.4.2"
env: TOXENV=lint
+ - python: "3.4.2"
+ env: TOXENV=typing
- python: "3.5"
env: TOXENV=py35
- python: "3.6"
diff --git a/README.md b/README.md
index 264c440..949a3ec 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ Current methods of scanning:
It is the library that powers the device discovery within [Home Assistant](https://home-assistant.io/).
+### We are no longer accepting PRs that implement custom discovery protocols. Only PRs that use mDNS or uPnP are supported. See [this issue](https://github.com/home-assistant/netdisco/issues/230)
+
## Installation
Netdisco is available on PyPi. Install using `pip3 install netdisco`.
diff --git a/netdisco/daikin.py b/netdisco/daikin.py
index a8e6e8f..014f1e2 100644
--- a/netdisco/daikin.py
+++ b/netdisco/daikin.py
@@ -2,6 +2,7 @@
import socket
from datetime import timedelta
+from typing import Dict, List # noqa: F401
from urllib.parse import unquote
DISCOVERY_MSG = b"DAIKIN_UDP/common/basic_info"
@@ -18,7 +19,7 @@ class Daikin:
def __init__(self):
"""Initialize the Daikin discovery."""
- self.entries = []
+ self.entries = [] # type: List[Dict[str, str]]
def scan(self):
"""Scan the network."""
@@ -47,9 +48,9 @@ class Daikin:
try:
data, (address, _) = sock.recvfrom(1024)
- # pylint: disable=consider-using-dict-comprehension
- entry = dict([e.split('=')
- for e in data.decode("UTF-8").split(',')])
+ entry = {x[0]: x[1] for x in (
+ e.split('=', 1)
+ for e in data.decode("UTF-8").split(','))}
# expecting product, mac, activation code, version
if 'ret' not in entry or entry['ret'] != 'OK':
diff --git a/netdisco/discoverables/__init__.py b/netdisco/discoverables/__init__.py
index dd39785..2cbf7be 100644
--- a/netdisco/discoverables/__init__.py
+++ b/netdisco/discoverables/__init__.py
@@ -1,6 +1,7 @@
"""Provides helpful stuff for discoverables."""
# pylint: disable=abstract-method
import ipaddress
+from typing import Dict, TYPE_CHECKING # noqa: F401
from urllib.parse import urlparse
from ..const import (
@@ -8,6 +9,9 @@ from ..const import (
ATTR_SERIAL, ATTR_MODEL_NUMBER, ATTR_HOSTNAME, ATTR_MAC_ADDRESS,
ATTR_PROPERTIES, ATTR_MANUFACTURER, ATTR_UDN, ATTR_UPNP_DEVICE_TYPE)
+if TYPE_CHECKING:
+ from zeroconf import ServiceInfo # noqa: F401
+
class BaseDiscoverable:
"""Base class for discoverable services or device types."""
@@ -80,7 +84,7 @@ class MDNSDiscoverable(BaseDiscoverable):
"""Initialize MDNSDiscoverable."""
self.netdis = netdis
self.typ = typ
- self.services = {}
+ self.services = {} # type: Dict[str, ServiceInfo]
netdis.mdns.register_service(self)
diff --git a/netdisco/discoverables/esphome.py b/netdisco/discoverables/esphome.py
new file mode 100644
index 0000000..faeac97
--- /dev/null
+++ b/netdisco/discoverables/esphome.py
@@ -0,0 +1,9 @@
+"""Discover ESPHome devices."""
+from . import MDNSDiscoverable
+
+
+class Discoverable(MDNSDiscoverable):
+ """Add support for discovering ESPHome devices."""
+
+ def __init__(self, nd):
+ super().__init__(nd, '_esphomelib._tcp.local.')
diff --git a/netdisco/discoverables/openhome.py b/netdisco/discoverables/openhome.py
index 3cc4314..7adca6f 100644
--- a/netdisco/discoverables/openhome.py
+++ b/netdisco/discoverables/openhome.py
@@ -7,4 +7,5 @@ class Discoverable(SSDPDiscoverable):
def get_entries(self):
"""Get all the Openhome compliant device uPnP entries."""
- return self.find_by_st("urn:av-openhome-org:service:Product:2")
+ return self.find_by_st("urn:av-openhome-org:service:Product:2") + \
+ self.find_by_st("urn:av-openhome-org:service:Product:3")
diff --git a/netdisco/discoverables/wink.py b/netdisco/discoverables/wink.py
index 9e6833f..3678d60 100644
--- a/netdisco/discoverables/wink.py
+++ b/netdisco/discoverables/wink.py
@@ -1,5 +1,8 @@
"""Discover Wink hub devices."""
+from typing import List # noqa: F401
+
from . import SSDPDiscoverable
+from ..ssdp import UPNPEntry # noqa: F401
class Discoverable(SSDPDiscoverable):
@@ -7,7 +10,7 @@ class Discoverable(SSDPDiscoverable):
def get_entries(self):
"""Return all Wink entries."""
- results = []
+ results = [] # type: List[UPNPEntry]
results.extend(self.find_by_st('urn:wink-com:device:hub2:2'))
results.extend(self.find_by_st('urn:wink-com:device:hub:2'))
results.extend(self.find_by_st('urn:wink-com:device:relay:2'))
diff --git a/netdisco/discovery.py b/netdisco/discovery.py
index 27bce19..39c4fda 100644
--- a/netdisco/discovery.py
+++ b/netdisco/discovery.py
@@ -123,7 +123,8 @@ class NetworkDiscovery:
module = importlib.import_module(
discoverables_format.format(module_name))
- self.discoverables[module_name] = module.Discoverable(self)
+ self.discoverables[module_name] = \
+ getattr(module, 'Discoverable')(self)
def print_raw_data(self):
"""Helper method to show what is discovered in your network."""
diff --git a/netdisco/gdm.py b/netdisco/gdm.py
index ce81ff0..75428b2 100644
--- a/netdisco/gdm.py
+++ b/netdisco/gdm.py
@@ -8,13 +8,14 @@ Inspired by
"""
import socket
import struct
+from typing import Any, Dict, List # noqa: F401
class GDM:
"""Base class to discover GDM services."""
def __init__(self):
- self.entries = []
+ self.entries = [] # type: List[Dict[str, Any]]
self.last_scan = None
def scan(self):
@@ -81,13 +82,13 @@ class GDM:
# Look for responses from all recipients
while True:
try:
- data, server = sock.recvfrom(1024)
- data = data.decode('utf-8')
+ bdata, server = sock.recvfrom(1024)
+ data = bdata.decode('utf-8')
if '200 OK' in data.splitlines()[0]:
- data = {k: v.strip() for (k, v) in (
+ ddata = {k: v.strip() for (k, v) in (
line.split(':') for line in
data.splitlines() if ':' in line)}
- self.entries.append({'data': data,
+ self.entries.append({'data': ddata,
'from': server})
except socket.timeout:
break
diff --git a/netdisco/lms.py b/netdisco/lms.py
index 6026a86..71934c9 100644
--- a/netdisco/lms.py
+++ b/netdisco/lms.py
@@ -1,5 +1,6 @@
"""Squeezebox/Logitech Media server discovery."""
import socket
+from typing import Dict, List, Union # noqa: F401
from .const import ATTR_HOST, ATTR_PORT
@@ -12,7 +13,7 @@ class LMS:
def __init__(self):
"""Initialize the Logitech discovery."""
- self.entries = []
+ self.entries = [] # type: List[Dict[str, Union[str, int]]]
self.last_scan = None
def scan(self):
@@ -49,7 +50,8 @@ class LMS:
# Where YY is length of port string (ie 4)
# And XXXX is the web interface port (ie 9000)
port = None
- if data.startswith(b'JSON', 1):
+ # https://github.com/python/typeshed/pull/2696
+ if data.startswith(b'JSON', 1): # type: ignore
length = data[5:6][0]
port = int(data[0-length:])
entries.append({
diff --git a/netdisco/mdns.py b/netdisco/mdns.py
index 8826346..c2cf8b1 100644
--- a/netdisco/mdns.py
+++ b/netdisco/mdns.py
@@ -1,4 +1,6 @@
"""Add support for discovering mDNS services."""
+from typing import List # noqa: F401
+
import zeroconf
@@ -8,8 +10,8 @@ class MDNS:
def __init__(self):
"""Initialize the discovery."""
self.zeroconf = None
- self.services = []
- self._browsers = []
+ self.services = [] # type: List[zeroconf.ServiceInfo]
+ self._browsers = [] # type: List[zeroconf.ServiceBrowser]
def register_service(self, service):
"""Register a mDNS service."""
diff --git a/netdisco/service.py b/netdisco/service.py
index 3cfb6b1..27fc0f9 100644
--- a/netdisco/service.py
+++ b/netdisco/service.py
@@ -3,6 +3,7 @@ import logging
import threading
import time
from collections import defaultdict
+from typing import Any, Callable, Dict, List # noqa: F401
from .discovery import NetworkDiscovery
@@ -25,7 +26,7 @@ class DiscoveryService(threading.Thread):
self.interval = interval
# Listeners for new services
- self.listeners = []
+ self.listeners = [] # type: List[Callable[[str, Any], None]]
# To track when we have to stop
self._stop = threading.Event()
@@ -38,7 +39,7 @@ class DiscoveryService(threading.Thread):
# Dict to keep track of found services. We do not want to
# broadcast the same found service twice.
- self._found = defaultdict(list)
+ self._found = defaultdict(list) # type: Dict[str, List]
def add_listener(self, listener):
"""Add a listener for new services."""
diff --git a/netdisco/smartglass.py b/netdisco/smartglass.py
index 4244a2b..0b534cf 100644
--- a/netdisco/smartglass.py
+++ b/netdisco/smartglass.py
@@ -3,6 +3,7 @@ import socket
import struct
import binascii
from datetime import timedelta
+from typing import Any, Dict, List, Optional, Tuple # noqa: F401
DISCOVERY_PORT = 5050
@@ -25,13 +26,15 @@ Android = 8
"""
DISCOVERY_CLIENT_TYPE = 4
+_Response = Dict[str, Any]
+
class XboxSmartGlass:
"""Base class to discover Xbox SmartGlass devices."""
def __init__(self):
"""Initialize the Xbox SmartGlass discovery."""
- self.entries = []
+ self.entries = [] # type: List[Tuple[str, Optional[_Response]]]
self._discovery_payload = self.discovery_packet()
@staticmethod
diff --git a/netdisco/ssdp.py b/netdisco/ssdp.py
index 55cb08b..7c4dd9c 100644
--- a/netdisco/ssdp.py
+++ b/netdisco/ssdp.py
@@ -4,6 +4,7 @@ import select
import socket
import logging
from datetime import datetime, timedelta
+from typing import Dict, List, Optional, Set # noqa: F401
from xml.etree import ElementTree
import requests
@@ -33,7 +34,7 @@ class SSDP:
def __init__(self):
"""Initialize the discovery."""
- self.entries = []
+ self.entries = [] # type: List[UPNPEntry]
self.last_scan = None
def scan(self):
@@ -65,7 +66,7 @@ class SSDP:
"""
self.update()
- seen = set()
+ seen = set() # type: Set[Optional[str]]
results = []
# Make unique based on the location since we don't care about ST here
@@ -100,7 +101,7 @@ class SSDP:
class UPNPEntry:
"""Found uPnP entry."""
- DESCRIPTION_CACHE = {'_NO_LOCATION': {}}
+ DESCRIPTION_CACHE = {'_NO_LOCATION': {}} # type: Dict[str, Dict]
def __init__(self, values):
"""Initialize the discovery."""
diff --git a/netdisco/tellstick.py b/netdisco/tellstick.py
index 24cf243..f5bd7bd 100644
--- a/netdisco/tellstick.py
+++ b/netdisco/tellstick.py
@@ -2,6 +2,7 @@
import socket
from datetime import timedelta
import logging
+from typing import List, Tuple # noqa: F401
DISCOVERY_PORT = 30303
@@ -15,7 +16,7 @@ class Tellstick:
def __init__(self):
"""Initialize the Tellstick discovery."""
- self.entries = []
+ self.entries = [] # type: List[Tuple[str]]
def scan(self):
"""Scan the network."""
@@ -43,8 +44,8 @@ class Tellstick:
# expecting product, mac, activation code, version
if len(entry) != 4:
continue
- entry = (address,) + tuple(entry)
- entries.append(entry)
+ entry.insert(0, address)
+ entries.append(tuple(entry))
except socket.timeout:
break
diff --git a/netdisco/util.py b/netdisco/util.py
index 65475b4..445062d 100644
--- a/netdisco/util.py
+++ b/netdisco/util.py
@@ -1,5 +1,6 @@
"""Util functions used by Netdisco."""
from collections import defaultdict
+from typing import Any, Dict, List, Optional # noqa: F401
# Taken from http://stackoverflow.com/a/10077069
@@ -9,21 +10,26 @@ def etree_to_dict(t):
# strip namespace
tag_name = t.tag[t.tag.find("}")+1:]
- d = {tag_name: {} if t.attrib else None}
+ d = {
+ tag_name: {} if t.attrib else None
+ } # type: Dict[str, Optional[Dict[str, Any]]]
children = list(t)
if children:
- dd = defaultdict(list)
+ dd = defaultdict(list) # type: Dict[str, List]
for dc in map(etree_to_dict, children):
for k, v in dc.items():
dd[k].append(v)
d = {tag_name: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
+ dt = d[tag_name]
if t.attrib:
- d[tag_name].update(('@' + k, v) for k, v in t.attrib.items())
+ assert dt is not None
+ dt.update(('@' + k, v) for k, v in t.attrib.items())
if t.text:
text = t.text.strip()
if children or t.attrib:
if text:
- d[tag_name]['#text'] = text
+ assert dt is not None
+ dt['#text'] = text
else:
d[tag_name] = text
return d
diff --git a/requirements.txt b/requirements.txt
index 9ad458c..fbb8f3a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
zeroconf>=0.21.0
requests>=2.0
+typing; python_version<'3.5'
diff --git a/setup.cfg b/setup.cfg
index e48ddf6..e443a40 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,16 @@
-[bdist_wheel]
-universal = 1
-
[tool:pytest]
testpaths = tests
norecursedirs = .git
+
+[mypy]
+check_untyped_defs = true
+# TODO disallow_untyped_calls = true
+# TODO disallow_untyped_defs = true
+follow_imports = silent
+ignore_missing_imports = true
+no_implicit_optional = true
+warn_incomplete_stub = true
+warn_redundant_casts = true
+warn_return_any = true
+warn_unused_configs = true
+warn_unused_ignores = true
diff --git a/setup.py b/setup.py
index aaf08bb..7fe1e70 100644
--- a/setup.py
+++ b/setup.py
@@ -1,13 +1,35 @@
+"""Setup file for netdisco."""
+import os
from setuptools import setup, find_packages
-setup(name='netdisco',
- version='2.2.0',
- description='Discover devices on your local network',
- url='https://github.com/home-assistant/netdisco',
- author='Paulus Schoutsen',
- author_email='Paulus@PaulusSchoutsen.nl',
- license='Apache License 2.0',
- install_requires=['requests>=2.0', 'zeroconf>=0.21.0'],
- python_requires='>=3',
- packages=find_packages(exclude=['tests', 'tests.*']),
- zip_safe=False)
+here = os.path.abspath(os.path.dirname(__file__))
+
+with open(os.path.join(here, 'README.md'), encoding='utf-8') as readme_file:
+ long_description = readme_file.read()
+
+setup(
+ name='netdisco',
+ version='2.3.0',
+ description='Discover devices on your local network',
+ long_description=long_description,
+ long_description_content_type='text/markdown',
+ url='https://github.com/home-assistant/netdisco',
+ author='Paulus Schoutsen',
+ author_email='Paulus@PaulusSchoutsen.nl',
+ license='Apache License 2.0',
+ install_requires=['requests>=2.0', 'zeroconf>=0.21.0'],
+ python_requires='>=3',
+ packages=find_packages(exclude=['tests', 'tests.*']),
+ zip_safe=False,
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: POSIX',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Utilities',
+ 'Topic :: Home Automation',
+ 'Topic :: System :: Networking',
+ ],
+)
diff --git a/tox.ini b/tox.ini
index 03232c8..714a850 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py34, py35, py36, py37, lint
+envlist = py34, py35, py36, py37, lint, typing
skip_missing_interpreters = True
[testenv]
@@ -18,3 +18,9 @@ deps =
commands =
flake8 netdisco tests setup.py example_service.py
pylint netdisco tests
+
+[testenv:typing]
+deps =
+ mypy>=0.650
+commands =
+ mypy netdisco tests setup.py example_service.py