Data received from Tomato is now parsed using the json decoder instead of exec

This commit is contained in:
Paulus Schoutsen 2013-09-28 12:40:13 -07:00
parent 2460ff748e
commit 11529a473a

View File

@ -13,6 +13,8 @@ import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
import threading import threading
import time import time
import re
import json
import requests import requests
import ephem import ephem
@ -191,8 +193,7 @@ class DeviceTracker(object):
for device in temp_devices_to_track } for device in temp_devices_to_track }
# Add categories to state machine and update last_seen attribute # Add categories to state machine and update last_seen attribute
# If we don't update now a change event will be fired on boot. initial_search = device_scanner.get_active_devices()
initial_search = device_scanner.scan_devices()
default_last_seen = datetime(1990, 1, 1) default_last_seen = datetime(1990, 1, 1)
@ -210,7 +211,7 @@ class DeviceTracker(object):
# Update all devices state # Update all devices state
statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME if len(initial_search) > 0 else DEVICE_STATE_NOT_HOME) statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME if len(initial_search) > 0 else DEVICE_STATE_NOT_HOME)
track_time_change(eventbus, lambda time: self.update_devices(device_scanner.scan_devices())) track_time_change(eventbus, lambda time: self.update_devices(device_scanner.get_active_devices()))
def device_state_categories(self): def device_state_categories(self):
@ -258,35 +259,15 @@ class TomatoDeviceScanner(object):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.lock = threading.Lock() self.lock = threading.Lock()
# Read known devices # Read known devices if file exists
if os.path.isfile(TOMATO_KNOWN_DEVICES_FILE): if os.path.isfile(TOMATO_KNOWN_DEVICES_FILE):
with open(TOMATO_KNOWN_DEVICES_FILE) as inp: with open(TOMATO_KNOWN_DEVICES_FILE) as inp:
known_devices = { row['mac']: row for row in csv.DictReader(inp) } self.known_devices = { row['mac']: row for row in csv.DictReader(inp) }
# Update known devices csv file for future use self._update_known_devices_file()
with open(TOMATO_KNOWN_DEVICES_FILE, 'a') as outp:
writer = csv.writer(outp)
# Query for new devices
exec(self._tomato_request("devlist"))
for name, _, mac, _ in dhcpd_lease:
if mac not in known_devices:
writer.writerow((mac, name, 0))
self.last_results = [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
self.date_updated = datetime.now()
# Create a dict with ID: NAME of the devices to track # Create a dict with ID: NAME of the devices to track
self.devices_to_track = dict() self.devices_to_track = {mac: info['name'] for mac, info in self.known_devices.items() if info['track'] == '1'}
for mac in known_devices:
if known_devices[mac]['track'] == '1':
self.devices_to_track[mac] = known_devices[mac]['name']
# Quicker way of the previous statement but it doesn't go together with exec:
# unqualified exec is not allowed in function '__init__' it contains a nested function with free variables
# self.devices_to_track = {mac: known_devices[mac]['name'] for mac in known_devices if known_devices[mac]['track'] == '1'}
if len(self.devices_to_track) == 0: if len(self.devices_to_track) == 0:
self.logger.warning("No devices to track. Please update {}.".format(TOMATO_KNOWN_DEVICES_FILE)) self.logger.warning("No devices to track. Please update {}.".format(TOMATO_KNOWN_DEVICES_FILE))
@ -295,55 +276,75 @@ class TomatoDeviceScanner(object):
""" Returns a ``dict`` with device_id: device_name values. """ """ Returns a ``dict`` with device_id: device_name values. """
return self.devices_to_track return self.devices_to_track
def scan_devices(self): def get_active_devices(self):
""" Scans for new devices and returns a list containing device_ids. """ """ Scans for new devices and returns a list containing device_ids. """
self.lock.acquire() self._update_tomato_info()
return [item[1] for item in self.last_results['wldev']]
def _update_tomato_info(self):
""" Ensures the information from the Tomato router is up to date.
Returns boolean if successful. """
# if date_updated is not defined (update has never ran) or the date is too old we scan for new data
if not hasattr(self,'date_updated') or datetime.now() - self.date_updated > TOMATO_MIN_TIME_BETWEEN_SCANS:
self.lock.acquire()
# We don't want to hammer the router. Only update if TOMATO_MIN_TIME_BETWEEN_SCANS has passed
if self.date_updated is None or datetime.now() - self.date_updated > TOMATO_MIN_TIME_BETWEEN_SCANS:
self.logger.info("Tomato:Scanning") self.logger.info("Tomato:Scanning")
try: try:
# Query for new devices req = requests.post('http://{}/update.cgi'.format(self.host),
exec(self._tomato_request("devlist")) data={'_http_id':self.http_id, 'exec':'devlist'},
auth=requests.auth.HTTPBasicAuth(self.username, self.password))
self.last_results = [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
self.date_updated = datetime.now() self.date_updated = datetime.now()
except Exception as e: """
Tomato API:
arplist contains a list of lists with items:
- ip (string)
- mac (string)
- iface (string)
wldev contains list of lists with items:
- iface (string)
- mac (string)
- rssi (int)
- tx (int)
- rx (int)
- quality (int)
- unknown_num (int)
dhcpd_lease contains a list of lists with items:
- name (string)
- ip (string)
- mac (string)
- lease_age (string)
"""
self.last_results = {param: json.loads(value.replace("'",'"'))
for param, value in re.findall(r"(?P<param>\w*) = (?P<value>.*);", req.text)
if param in ["wldev","dhcpd_lease"]}
except requests.ConnectionError:
self.logger.exception("Scanning failed") self.logger.exception("Scanning failed")
return False
self.lock.release() finally:
return self.last_results self.lock.release()
def _tomato_request(self, action):
""" Talk to the Tomato API. """
# Get router info
req = requests.post('http://{}/update.cgi'.format(self.host),
data={'_http_id':self.http_id, 'exec':action},
auth=requests.auth.HTTPBasicAuth(self.username, self.password))
return req.text
return True
""" def _update_known_devices_file(self):
Tomato API: """ Update known devices csv file. """
for ip, mac, iface in arplist: self._update_tomato_info()
pass
print wlnoise with open(TOMATO_KNOWN_DEVICES_FILE, 'a') as outp:
writer = csv.writer(outp)
print dhcpd_static for name, _, mac, _ in self.last_results['dhcpd_lease']:
if mac not in self.known_devices:
self.known_devices[mac] = {'name':name, 'track': '0'}
writer.writerow((mac, name, 0))
for iface, mac, rssi, tx, rx, quality, unknown_num in wldev:
print mac, quality
for name, ip, mac, lease in dhcpd_lease:
if name:
print name, ip
else:
print ip
"""