Merge balloob/dev with rmkraus/daemon

This commit is contained in:
Ryan Kraus 2015-09-01 02:23:50 -04:00
commit 1add38a195
4 changed files with 40 additions and 62 deletions

View File

@ -5,6 +5,12 @@ VOLUME /config
RUN pip3 install --no-cache-dir -r requirements_all.txt RUN pip3 install --no-cache-dir -r requirements_all.txt
# For the nmap tracker
RUN apt-get update && \
apt-get install -y --no-install-recommends nmap net-tools && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Open Z-Wave disabled because broken
#RUN apt-get update && \ #RUN apt-get update && \
# apt-get install -y cython3 libudev-dev && \ # apt-get install -y cython3 libudev-dev && \
# apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ # apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \

View File

@ -10,7 +10,6 @@ import os
import csv import csv
from datetime import timedelta from datetime import timedelta
from homeassistant.loader import get_component
from homeassistant.helpers import validate_config from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import _OVERWRITE from homeassistant.helpers.entity import _OVERWRITE
import homeassistant.util as util import homeassistant.util as util
@ -63,7 +62,7 @@ def setup(hass, config):
return False return False
tracker_type = config[DOMAIN].get(CONF_PLATFORM) tracker_type = config[DOMAIN].get(CONF_PLATFORM)
tracker_implementation = \ tracker_implementation = \
prepare_setup_platform(hass, config, DOMAIN, tracker_type) prepare_setup_platform(hass, config, DOMAIN, tracker_type)

View File

@ -19,6 +19,11 @@ hosts
*Required *Required
The IP addresses to scan in the network-prefix notation (192.168.1.1/24) or The IP addresses to scan in the network-prefix notation (192.168.1.1/24) or
the range notation (192.168.1.1-255). the range notation (192.168.1.1-255).
home_interval
*Optional
Number of minutes it will not scan devices that it found in previous results.
This is to save battery.
""" """
import logging import logging
from datetime import timedelta from datetime import timedelta
@ -40,22 +45,15 @@ _LOGGER = logging.getLogger(__name__)
# interval in minutes to exclude devices from a scan while they are home # interval in minutes to exclude devices from a scan while they are home
CONF_HOME_INTERVAL = "home_interval" CONF_HOME_INTERVAL = "home_interval"
REQUIREMENTS = ['python-libnmap==0.6.1'] REQUIREMENTS = ['python-nmap==0.4.1']
def get_scanner(hass, config): def get_scanner(hass, config):
""" Validates config and returns a Nmap scanner. """ """ Validates config and returns a Nmap scanner. """
# pylint: disable=unused-variable
if not validate_config(config, {DOMAIN: [CONF_HOSTS]}, if not validate_config(config, {DOMAIN: [CONF_HOSTS]},
_LOGGER): _LOGGER):
return None return None
try:
import libnmap # noqa
except ImportError:
_LOGGER.error("Error while importing dependency python-libnmap.")
return None
scanner = NmapDeviceScanner(config[DOMAIN]) scanner = NmapDeviceScanner(config[DOMAIN])
return scanner if scanner.success_init else None return scanner if scanner.success_init else None
@ -72,28 +70,20 @@ def _arp(ip_address):
if match: if match:
return match.group(0) return match.group(0)
_LOGGER.info("No MAC address found for %s", ip_address) _LOGGER.info("No MAC address found for %s", ip_address)
return '' return None
class NmapDeviceScanner(object): class NmapDeviceScanner(object):
""" This class scans for devices using nmap """ """ This class scans for devices using nmap """
def __init__(self, config): def __init__(self, config):
from libnmap.process import NmapProcess
from libnmap.parser import NmapParser, NmapParserException
self.nmap_process = NmapProcess
self.nmap_parser = NmapParser
self.nmap_parser_exception = NmapParserException
self.last_results = [] self.last_results = []
self.hosts = config[CONF_HOSTS] self.hosts = config[CONF_HOSTS]
minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0) minutes = convert(config.get(CONF_HOME_INTERVAL), int, 0)
self.home_interval = timedelta(minutes=minutes) self.home_interval = timedelta(minutes=minutes)
self.success_init = True self.success_init = self._update_info()
self._update_info()
_LOGGER.info("nmap scanner initialized") _LOGGER.info("nmap scanner initialized")
def scan_devices(self): def scan_devices(self):
@ -115,43 +105,16 @@ class NmapDeviceScanner(object):
else: else:
return None return None
def _parse_results(self, stdout):
""" Parses results from an nmap scan.
Returns True if successful, False otherwise. """
try:
results = self.nmap_parser.parse(stdout)
now = dt_util.now()
self.last_results = []
for host in results.hosts:
if host.is_up():
if host.hostnames:
name = host.hostnames[0]
else:
name = host.ipv4
if host.mac:
mac = host.mac
else:
mac = _arp(host.ipv4)
if mac:
device = Device(mac.upper(), name, host.ipv4, now)
self.last_results.append(device)
_LOGGER.info("nmap scan successful")
return True
except self.nmap_parser_exception as parse_exc:
_LOGGER.error("failed to parse nmap results: %s", parse_exc.msg)
self.last_results = []
return False
@Throttle(MIN_TIME_BETWEEN_SCANS) @Throttle(MIN_TIME_BETWEEN_SCANS)
def _update_info(self): def _update_info(self):
""" Scans the network for devices. """ Scans the network for devices.
Returns boolean if scanning successful. """ Returns boolean if scanning successful. """
if not self.success_init:
return False
_LOGGER.info("Scanning") _LOGGER.info("Scanning")
options = "-F --host-timeout 5" from nmap import PortScanner, PortScannerError
scanner = PortScanner()
options = "-sP --host-timeout 5"
exclude_targets = set() exclude_targets = set()
if self.home_interval: if self.home_interval:
now = dt_util.now() now = dt_util.now()
@ -162,14 +125,24 @@ class NmapDeviceScanner(object):
target_list = [t.ip for t in exclude_targets] target_list = [t.ip for t in exclude_targets]
options += " --exclude {}".format(",".join(target_list)) options += " --exclude {}".format(",".join(target_list))
nmap = self.nmap_process(targets=self.hosts, options=options) try:
result = scanner.scan(hosts=self.hosts, arguments=options)
nmap.run() except PortScannerError:
if nmap.rc == 0:
if self._parse_results(nmap.stdout):
self.last_results.extend(exclude_targets)
else:
self.last_results = []
_LOGGER.error(nmap.stderr)
return False return False
now = dt_util.now()
self.last_results = []
for ipv4, info in result['scan'].items():
if info['status']['state'] != 'up':
continue
name = info['hostnames'][0] if info['hostnames'] else ipv4
# Mac address only returned if nmap ran as root
mac = info['addresses'].get('mac') or _arp(ipv4)
if mac is None:
continue
device = Device(mac.upper(), name, ipv4, now)
self.last_results.append(device)
self.last_results.extend(exclude_targets)
_LOGGER.info("nmap scan successful")
return True

View File

@ -25,7 +25,7 @@ pyuserinput==0.1.9
tellcore-py==1.0.4 tellcore-py==1.0.4
# Nmap bindings (device_tracker.nmap) # Nmap bindings (device_tracker.nmap)
python-libnmap==0.6.3 python-nmap==0.4.1
# PushBullet bindings (notify.pushbullet) # PushBullet bindings (notify.pushbullet)
pushbullet.py==0.7.1 pushbullet.py==0.7.1