Skip to content

Commit

Permalink
Add Freebox device tracker (home-assistant#12727)
Browse files Browse the repository at this point in the history
* Add a device tracker for Freebox routers

* Automatic setup of Freebox device tracker based on discovery

* Make the Freebox device tracker asynchronous
  • Loading branch information
stilllman authored and syssi committed Jun 5, 2018
1 parent 549abd9 commit 1036394
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ omit =
homeassistant/components/device_tracker/bt_home_hub_5.py
homeassistant/components/device_tracker/cisco_ios.py
homeassistant/components/device_tracker/ddwrt.py
homeassistant/components/device_tracker/freebox.py
homeassistant/components/device_tracker/fritz.py
homeassistant/components/device_tracker/google_maps.py
homeassistant/components/device_tracker/gpslogger.py
Expand Down
120 changes: 120 additions & 0 deletions homeassistant/components/device_tracker/freebox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
Support for device tracking through Freebox routers.
This tracker keeps track of the devices connected to the configured Freebox.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.freebox/
"""
import asyncio
import copy
import logging
import socket
from collections import namedtuple
from datetime import timedelta

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
from homeassistant.const import (
CONF_HOST, CONF_PORT)

REQUIREMENTS = ['aiofreepybox==0.0.3']

_LOGGER = logging.getLogger(__name__)

FREEBOX_CONFIG_FILE = 'freebox.conf'

PLATFORM_SCHEMA = vol.All(
PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.port
}))

MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)


async def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up the Freebox device tracker and start the polling."""
freebox_config = copy.deepcopy(config)
if discovery_info is not None:
freebox_config[CONF_HOST] = discovery_info['properties']['api_domain']
freebox_config[CONF_PORT] = discovery_info['properties']['https_port']
_LOGGER.info("Discovered Freebox server: %s:%s",
freebox_config[CONF_HOST], freebox_config[CONF_PORT])

scanner = FreeboxDeviceScanner(hass, freebox_config, async_see)
interval = freebox_config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
await scanner.async_start(hass, interval)
return True


Device = namedtuple('Device', ['id', 'name', 'ip'])


def _build_device(device_dict):
return Device(
device_dict['l2ident']['id'],
device_dict['primary_name'],
device_dict['l3connectivities'][0]['addr'])


class FreeboxDeviceScanner(object):
"""This class scans for devices connected to the Freebox."""

def __init__(self, hass, config, async_see):
"""Initialize the scanner."""
from aiofreepybox import Freepybox

self.host = config[CONF_HOST]
self.port = config[CONF_PORT]
self.token_file = hass.config.path(FREEBOX_CONFIG_FILE)
self.async_see = async_see

# Hardcode the app description to avoid invalidating the authentication
# file at each new version.
# The version can be changed if we want the user to re-authorize HASS
# on her Freebox.
app_desc = {
'app_id': 'hass',
'app_name': 'Home Assistant',
'app_version': '0.65',
'device_name': socket.gethostname()
}

api_version = 'v1' # Use the lowest working version.
self.fbx = Freepybox(
app_desc=app_desc,
token_file=self.token_file,
api_version=api_version)

async def async_start(self, hass, interval):
"""Perform a first update and start polling at the given interval."""
await self.async_update_info()
interval = max(interval, MIN_TIME_BETWEEN_SCANS)
async_track_time_interval(hass, self.async_update_info, interval)

async def async_update_info(self, now=None):
"""Check the Freebox for devices."""
from aiofreepybox.exceptions import HttpRequestError

_LOGGER.info('Scanning devices')

await self.fbx.open(self.host, self.port)
try:
hosts = await self.fbx.lan.get_hosts_list()
except HttpRequestError:
_LOGGER.exception('Failed to scan devices')
else:
active_devices = [_build_device(device)
for device in hosts
if device['active']]

if active_devices:
await asyncio.wait([self.async_see(mac=d.id, host_name=d.name)
for d in active_devices])

await self.fbx.close()
1 change: 1 addition & 0 deletions homeassistant/components/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
'kodi': ('media_player', 'kodi'),
'volumio': ('media_player', 'volumio'),
'nanoleaf_aurora': ('light', 'nanoleaf_aurora'),
'freebox': ('device_tracker', 'freebox'),
}

OPTIONAL_SERVICE_HANDLERS = {
Expand Down
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ aioautomatic==0.6.5
# homeassistant.components.sensor.dnsip
aiodns==1.1.1

# homeassistant.components.device_tracker.freebox
aiofreepybox==0.0.3

# homeassistant.components.emulated_hue
# homeassistant.components.http
aiohttp_cors==0.7.0
Expand Down

0 comments on commit 1036394

Please sign in to comment.