141 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
"""Support for the Hitron CODA-4582U, provided by Rogers."""
 | 
						|
from __future__ import annotations
 | 
						|
 | 
						|
from collections import namedtuple
 | 
						|
from http import HTTPStatus
 | 
						|
import logging
 | 
						|
 | 
						|
import requests
 | 
						|
import voluptuous as vol
 | 
						|
 | 
						|
from homeassistant.components.device_tracker import (
 | 
						|
    DOMAIN,
 | 
						|
    PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
 | 
						|
    DeviceScanner,
 | 
						|
)
 | 
						|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_TYPE, CONF_USERNAME
 | 
						|
from homeassistant.core import HomeAssistant
 | 
						|
import homeassistant.helpers.config_validation as cv
 | 
						|
from homeassistant.helpers.typing import ConfigType
 | 
						|
 | 
						|
_LOGGER = logging.getLogger(__name__)
 | 
						|
 | 
						|
DEFAULT_TYPE = "rogers"
 | 
						|
 | 
						|
PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
 | 
						|
    {
 | 
						|
        vol.Required(CONF_HOST): cv.string,
 | 
						|
        vol.Required(CONF_USERNAME): cv.string,
 | 
						|
        vol.Required(CONF_PASSWORD): cv.string,
 | 
						|
        vol.Optional(CONF_TYPE, default=DEFAULT_TYPE): cv.string,
 | 
						|
    }
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
def get_scanner(
 | 
						|
    _hass: HomeAssistant, config: ConfigType
 | 
						|
) -> HitronCODADeviceScanner | None:
 | 
						|
    """Validate the configuration and return a Hitron CODA-4582U scanner."""
 | 
						|
    scanner = HitronCODADeviceScanner(config[DOMAIN])
 | 
						|
 | 
						|
    return scanner if scanner.success_init else None
 | 
						|
 | 
						|
 | 
						|
Device = namedtuple("Device", ["mac", "name"])
 | 
						|
 | 
						|
 | 
						|
class HitronCODADeviceScanner(DeviceScanner):
 | 
						|
    """Scanner for devices using the CODA's web interface."""
 | 
						|
 | 
						|
    def __init__(self, config):
 | 
						|
        """Initialize the scanner."""
 | 
						|
        self.last_results = []
 | 
						|
        host = config[CONF_HOST]
 | 
						|
        self._url = f"http://{host}/data/getConnectInfo.asp"
 | 
						|
        self._loginurl = f"http://{host}/goform/login"
 | 
						|
 | 
						|
        self._username = config.get(CONF_USERNAME)
 | 
						|
        self._password = config.get(CONF_PASSWORD)
 | 
						|
 | 
						|
        if config.get(CONF_TYPE) == "shaw":
 | 
						|
            self._type = "pwd"
 | 
						|
        else:
 | 
						|
            self._type = "pws"
 | 
						|
 | 
						|
        self._userid = None
 | 
						|
 | 
						|
        self.success_init = self._update_info()
 | 
						|
        _LOGGER.info("Scanner initialized")
 | 
						|
 | 
						|
    def scan_devices(self):
 | 
						|
        """Scan for new devices and return a list with found device IDs."""
 | 
						|
        self._update_info()
 | 
						|
 | 
						|
        return [device.mac for device in self.last_results]
 | 
						|
 | 
						|
    def get_device_name(self, device):
 | 
						|
        """Return the name of the device with the given MAC address."""
 | 
						|
        return next(
 | 
						|
            (result.name for result in self.last_results if result.mac == device), None
 | 
						|
        )
 | 
						|
 | 
						|
    def _login(self):
 | 
						|
        """Log in to the router. This is required for subsequent api calls."""
 | 
						|
        _LOGGER.info("Logging in to CODA")
 | 
						|
 | 
						|
        try:
 | 
						|
            data = [("user", self._username), (self._type, self._password)]
 | 
						|
            res = requests.post(self._loginurl, data=data, timeout=10)
 | 
						|
        except requests.exceptions.Timeout:
 | 
						|
            _LOGGER.error("Connection to the router timed out at URL %s", self._url)
 | 
						|
            return False
 | 
						|
        if res.status_code != HTTPStatus.OK:
 | 
						|
            _LOGGER.error("Connection failed with http code %s", res.status_code)
 | 
						|
            return False
 | 
						|
        try:
 | 
						|
            self._userid = res.cookies["userid"]
 | 
						|
            return True
 | 
						|
        except KeyError:
 | 
						|
            _LOGGER.error("Failed to log in to router")
 | 
						|
            return False
 | 
						|
 | 
						|
    def _update_info(self):
 | 
						|
        """Get ARP from router."""
 | 
						|
        _LOGGER.info("Fetching")
 | 
						|
 | 
						|
        if self._userid is None and not self._login():
 | 
						|
            _LOGGER.error("Could not obtain a user ID from the router")
 | 
						|
            return False
 | 
						|
        last_results = []
 | 
						|
 | 
						|
        # doing a request
 | 
						|
        try:
 | 
						|
            res = requests.get(self._url, timeout=10, cookies={"userid": self._userid})
 | 
						|
        except requests.exceptions.Timeout:
 | 
						|
            _LOGGER.error("Connection to the router timed out at URL %s", self._url)
 | 
						|
            return False
 | 
						|
        if res.status_code != HTTPStatus.OK:
 | 
						|
            _LOGGER.error("Connection failed with http code %s", res.status_code)
 | 
						|
            return False
 | 
						|
        try:
 | 
						|
            result = res.json()
 | 
						|
        except ValueError:
 | 
						|
            # If json decoder could not parse the response
 | 
						|
            _LOGGER.error("Failed to parse response from router")
 | 
						|
            return False
 | 
						|
 | 
						|
        # parsing response
 | 
						|
        for info in result:
 | 
						|
            mac = info["macAddr"]
 | 
						|
            name = info["hostName"]
 | 
						|
            # No address = no item :)
 | 
						|
            if mac is None:
 | 
						|
                continue
 | 
						|
 | 
						|
            last_results.append(Device(mac.upper(), name))
 | 
						|
 | 
						|
        self.last_results = last_results
 | 
						|
 | 
						|
        _LOGGER.info("Request successful")
 | 
						|
        return True
 |