* Upgrade pylint to 1.8.1 * Fix no-else-return * Fix bad-whitespace * Fix too-many-nested-blocks * Fix raising-format-tuple See https://github.com/PyCQA/pylint/blob/master/doc/whatsnew/1.8.rst * Fix len-as-condition * Fix logging-not-lazy Not sure about that TEMP_CELSIUS though, but internally it's probably just like if you concatenated any other (variable) string * Fix stop-iteration-return * Fix useless-super-delegation * Fix trailing-comma-tuple Both of these seem to simply be bugs: * Nest: The value of self._humidity never seems to be used anywhere * Dovado: The called API method seems to expect a "normal" number * Fix redefined-argument-from-local * Fix consider-using-enumerate * Fix wrong-import-order * Fix arguments-differ * Fix missed no-else-return * Fix no-member and related * Fix signatures-differ * Revert "Upgrade pylint to 1.8.1" This reverts commit af78aa00f125a7d34add97b9d50c14db48412211. * Fix arguments-differ * except for device_tracker * Cleanup * Fix test using positional argument * Fix line too long I forgot to run flake8 - shame on me... 🙃 * Fix bad-option-value for 1.6.5 * Fix arguments-differ for device_tracker * Upgrade pylint to 1.8.2 * 👕 Fix missed no-member
105 lines
3.5 KiB
Python
105 lines
3.5 KiB
Python
"""Support for Linksys Smart Wifi routers."""
|
|
import logging
|
|
|
|
import requests
|
|
import voluptuous as vol
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
|
from homeassistant.components.device_tracker import (
|
|
DOMAIN, PLATFORM_SCHEMA, DeviceScanner)
|
|
from homeassistant.const import CONF_HOST
|
|
|
|
DEFAULT_TIMEOUT = 10
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|
vol.Required(CONF_HOST): cv.string,
|
|
})
|
|
|
|
|
|
def get_scanner(hass, config):
|
|
"""Validate the configuration and return a Linksys AP scanner."""
|
|
try:
|
|
return LinksysSmartWifiDeviceScanner(config[DOMAIN])
|
|
except ConnectionError:
|
|
return None
|
|
|
|
|
|
class LinksysSmartWifiDeviceScanner(DeviceScanner):
|
|
"""This class queries a Linksys Access Point."""
|
|
|
|
def __init__(self, config):
|
|
"""Initialize the scanner."""
|
|
self.host = config[CONF_HOST]
|
|
self.last_results = {}
|
|
|
|
# Check if the access point is accessible
|
|
response = self._make_request()
|
|
if not response.status_code == 200:
|
|
raise ConnectionError("Cannot connect to Linksys Access Point")
|
|
|
|
def scan_devices(self):
|
|
"""Scan for new devices and return a list with device IDs (MACs)."""
|
|
self._update_info()
|
|
|
|
return self.last_results.keys()
|
|
|
|
def get_device_name(self, device):
|
|
"""Return the name (if known) of the device."""
|
|
return self.last_results.get(device)
|
|
|
|
def _update_info(self):
|
|
"""Check for connected devices."""
|
|
_LOGGER.info("Checking Linksys Smart Wifi")
|
|
|
|
self.last_results = {}
|
|
response = self._make_request()
|
|
if response.status_code != 200:
|
|
_LOGGER.error(
|
|
"Got HTTP status code %d when getting device list",
|
|
response.status_code)
|
|
return False
|
|
try:
|
|
data = response.json()
|
|
result = data["responses"][0]
|
|
devices = result["output"]["devices"]
|
|
for device in devices:
|
|
macs = device["knownMACAddresses"]
|
|
if not macs:
|
|
_LOGGER.warning(
|
|
"Skipping device without known MAC address")
|
|
continue
|
|
mac = macs[-1]
|
|
connections = device["connections"]
|
|
if not connections:
|
|
_LOGGER.debug("Device %s is not connected", mac)
|
|
continue
|
|
|
|
name = None
|
|
for prop in device["properties"]:
|
|
if prop["name"] == "userDeviceName":
|
|
name = prop["value"]
|
|
if not name:
|
|
name = device.get("friendlyName", device["deviceID"])
|
|
|
|
_LOGGER.debug("Device %s is connected", mac)
|
|
self.last_results[mac] = name
|
|
except (KeyError, IndexError):
|
|
_LOGGER.exception("Router returned unexpected response")
|
|
return False
|
|
return True
|
|
|
|
def _make_request(self):
|
|
# Weirdly enough, this doesn't seem to require authentication
|
|
data = [{
|
|
"request": {
|
|
"sinceRevision": 0
|
|
},
|
|
"action": "http://linksys.com/jnap/devicelist/GetDevices"
|
|
}]
|
|
headers = {"X-JNAP-Action": "http://linksys.com/jnap/core/Transaction"}
|
|
return requests.post('http://{}/JNAP/'.format(self.host),
|
|
timeout=DEFAULT_TIMEOUT,
|
|
headers=headers,
|
|
json=data)
|