summaryrefslogtreecommitdiffstats
path: root/components/cloud_devices
diff options
context:
space:
mode:
authorvitalybuka@chromium.org <vitalybuka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-20 06:42:42 +0000
committervitalybuka@chromium.org <vitalybuka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-20 06:42:42 +0000
commit7b6f5dd4a511e98a8201e8084cba52ba1af1469e (patch)
treea5e45552326af1b86a1c4339a3d907e25f4d288c /components/cloud_devices
parent938f37f79f5d74702420e48210afecfdb210f198 (diff)
downloadchromium_src-7b6f5dd4a511e98a8201e8084cba52ba1af1469e.zip
chromium_src-7b6f5dd4a511e98a8201e8084cba52ba1af1469e.tar.gz
chromium_src-7b6f5dd4a511e98a8201e8084cba52ba1af1469e.tar.bz2
Fixed gpylint warnings.
TBR=noamsml NOTRY=true Review URL: https://codereview.chromium.org/292103002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271596 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/cloud_devices')
-rwxr-xr-xcomponents/cloud_devices/tools/prototype/prototype.py1644
1 files changed, 866 insertions, 778 deletions
diff --git a/components/cloud_devices/tools/prototype/prototype.py b/components/cloud_devices/tools/prototype/prototype.py
index 2e2d923..4b3d074 100755
--- a/components/cloud_devices/tools/prototype/prototype.py
+++ b/components/cloud_devices/tools/prototype/prototype.py
@@ -3,18 +3,20 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# This prototype has tons of flaws, not the least of which being that it
-# occasionally will block while waiting for commands to finish. However, this is
-# a quick sketch.
-# Script requires following components:
-# sudo apt-get install python-tornado
-# sudo apt-get install python-pip
-# sudo pip install google-api-python-client
+"""Prototype of cloud device with support of local API.
+
+ This prototype has tons of flaws, not the least of which being that it
+ occasionally will block while waiting for commands to finish. However, this is
+ a quick sketch.
+ Script requires following components:
+ sudo apt-get install python-tornado
+ sudo apt-get install python-pip
+ sudo pip install google-api-python-client
+"""
import atexit
import base64
import datetime
-import httplib2
import json
import os
import subprocess
@@ -23,6 +25,7 @@ import traceback
from apiclient.discovery import build_from_document
from apiclient.errors import HttpError
+import httplib2
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.file import Storage
@@ -33,25 +36,26 @@ _OAUTH_SCOPE = 'https://www.googleapis.com/auth/clouddevices'
_API_CLIENT_FILE = 'config.json'
_API_DISCOVERY_FILE = 'discovery.json'
+_DEVICE_STATE_FILE = 'device_state.json'
DEVICE_DRAFT = {
- 'systemName': 'LEDFlasher',
- 'deviceKind': 'vendor',
- 'displayName': 'LED Flasher',
- 'channel': {
- 'supportedType': 'xmpp'
- },
- 'commands': {
- 'base': {
- 'vendorCommands': [{
- 'name': 'flashLED',
- 'parameter' : [{
- 'name': 'times',
- 'type': 'string'
- }]
- }]
+ 'systemName': 'LEDFlasher',
+ 'deviceKind': 'vendor',
+ 'displayName': 'LED Flasher',
+ 'channel': {
+ 'supportedType': 'xmpp'
+ },
+ 'commands': {
+ 'base': {
+ 'vendorCommands': [{
+ 'name': 'flashLED',
+ 'parameter': [{
+ 'name': 'times',
+ 'type': 'string'
+ }]
+ }]
+ }
}
- }
}
wpa_supplicant_cmd = 'wpa_supplicant -Dwext -iwlan0 -cwpa_supplicant.conf'
@@ -65,843 +69,927 @@ wpa_supplicant_conf = 'wpa_supplicant.conf'
wpa_supplicant_template = """
network={
- ssid="%s"
- scan_ssid=1
- proto=WPA RSN
- key_mgmt=WPA-PSK
- pairwise=CCMP TKIP
- group=CCMP TKIP
- psk="%s"
+ ssid="%s"
+ scan_ssid=1
+ proto=WPA RSN
+ key_mgmt=WPA-PSK
+ pairwise=CCMP TKIP
+ group=CCMP TKIP
+ psk="%s"
}"""
led_path = '/sys/class/leds/ath9k_htc-phy0/'
+
class DeviceUnregisteredError(Exception):
- pass
+ pass
+
def ignore_errors(func):
- def inner(*args, **kwargs):
- try:
- func(*args, **kwargs)
- except:
- print 'Got error in unsafe function:'
- traceback.print_exc()
- return inner
+ def inner(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except Exception: # pylint: disable=broad-except
+ print 'Got error in unsafe function:'
+ traceback.print_exc()
+ return inner
+
class CommandWrapperReal(object):
- def __init__(self, cmd):
- if type(cmd) == str:
- cmd = cmd.split()
- self.cmd = cmd
- self.process = None
- def start(self):
- if self.process:
- end()
- self.process = subprocess.Popen(self.cmd)
- def wait(self):
- self.process.wait()
- def end(self):
- if self.process:
- self.process.terminate()
+ """Command wrapper that executs shell commands."""
+
+ def __init__(self, cmd):
+ if type(cmd) == str:
+ cmd = cmd.split()
+ self.cmd = cmd
+ self.process = None
+
+ def start(self):
+ if self.process:
+ self.end()
+ self.process = subprocess.Popen(self.cmd)
+
+ def wait(self):
+ self.process.wait()
+
+ def end(self):
+ if self.process:
+ self.process.terminate()
+
class CommandWrapperFake(object):
- def __init__(self, cmd):
- self.cmd = cmd
- def start(self):
- print 'Start: ', self.cmd
- def wait(self):
- print 'Wait: ', self.cmd
- def end(self):
- print 'End: ', self.cmd
+ """Command wrapper that just prints shell commands."""
+
+ def __init__(self, cmd):
+ self.cmd = cmd
+
+ def start(self):
+ print 'Start: ', self.cmd
+
+ def wait(self):
+ print 'Wait: ', self.cmd
+
+ def end(self):
+ print 'End: ', self.cmd
+
class CloudCommandHandlerFake(object):
- def __init__(self, ioloop):
- pass
- def handle_command(self, command_name, args):
- if command_name == 'flashLED':
- times = 1
- if 'times' in args:
- times = int(args['times'])
- print 'Flashing LED %d times' % times
+ """Prints devices commands without execution."""
+
+ def __init__(self, ioloop):
+ pass
+
+ def handle_command(self, command_name, args):
+ if command_name == 'flashLED':
+ times = 1
+ if 'times' in args:
+ times = int(args['times'])
+ print 'Flashing LED %d times' % times
+
class CloudCommandHandlerReal(object):
- def __init__(self, ioloop):
- self.ioloop = ioloop
- def handle_command(self, command_name, args):
- if command_name == 'flashLED':
- times = 1
- if 'times' in args:
- times = int(args['times'])
- print 'Really flashing LED %d times' % times
- self.flash_led(times)
- @ignore_errors
- def flash_led(self, times):
- self.set_led(times*2, True)
- def set_led(self, times, value):
- if not times:
- return
-
- file_trigger = open(os.path.join(led_path, 'brightness'), 'w')
-
- if value:
- file_trigger.write('1')
- else:
- file_trigger.write('0')
-
- file_trigger.close()
-
- self.ioloop.add_timeout(datetime.timedelta(milliseconds = 500),
- lambda: self.set_led(times - 1, not value))
+ """Executes device commands."""
-class WifiHandler(object):
- class Delegate:
- # Token is optional, and all delegates should support it being None
- def on_wifi_connected(self, token):
- raise Exception('Unhandled condition: WiFi connected')
-
- def __init__(self, ioloop, state, delegate):
- self.ioloop = ioloop
- self.state = state
- self.delegate = delegate
- def start(self):
- raise Exception('Start not implemented!')
- def get_ssid(self):
- raise Exception('Get SSID not implemented!')
-
-# Note that by using CommandWrapperFake, you can run WifiHandlerReal on fake
-# devices for testing the wifi-specific logic
-class WifiHandlerReal(WifiHandler):
- def __init__(self, ioloop, state, delegate):
- super(WifiHandlerReal, self).__init__(ioloop, state, delegate)
-
- self.hostapd = CommandWrapper(hostapd_cmd)
- self.wpa_supplicant = CommandWrapper(wpa_supplicant_cmd)
- self.dhcpd = CommandWrapper(dhcpd_cmd)
- def start(self):
- if self.state.has_wifi():
- self.switch_to_wifi(self.state.ssid(),
- self.state.password(),
- None)
- else:
- self.start_hostapd()
- def start_hostapd(self):
- self.hostapd.start()
- time.sleep(3)
- run_command(ifconfig_cmd)
- self.dhcpd.start()
- def switch_to_wifi(self, ssid, passwd, token):
- try:
- wpa_config = open(wpa_supplicant_conf, 'w')
- wpa_config.write(wpa_supplicant_template % (ssid, passwd))
- wpa_config.close()
- self.hostapd.end()
- self.dhcpd.end()
- self.wpa_supplicant.start()
- run_command(dhclient_release)
- run_command(dhclient_renew)
-
- self.state.set_wifi(ssid,passwd)
- self.delegate.on_wifi_connected(token)
- except DeviceUnregisteredError:
- self.state.reset()
- self.wpa_supplicant.end()
- self.start_hostapd()
- def stop(self):
- self.hostapd.end()
- self.wpa_supplicant.end()
- self.dhcpd.end()
- def get_ssid(self):
- return self.state.get_ssid()
+ def __init__(self, ioloop):
+ self.ioloop = ioloop
+ def handle_command(self, command_name, args):
+ if command_name == 'flashLED':
+ times = 1
+ if 'times' in args:
+ times = int(args['times'])
+ print 'Really flashing LED %d times' % times
+ self.flash_led(times)
-class WifiHandlerPassthrough(WifiHandler):
- def __init__(self, ioloop, state, delegate):
- super(WifiHandlerPassthrough, self).__init__(ioloop, state, delegate)
- def start(self):
- self.delegate.on_wifi_connected(None)
- def switch_to_wifi(self, ssid, passwd, token):
- raise Exception('Should not be reached')
- def get_ssid(self):
- return 'dummy'
+ @ignore_errors
+ def flash_led(self, times):
+ self.set_led(times*2, True)
+ def set_led(self, times, value):
+ """Set led value."""
+ if not times:
+ return
-def setup_fake():
- print 'Called setup'
+ file_trigger = open(os.path.join(led_path, 'brightness'), 'w')
+
+ if value:
+ file_trigger.write('1')
+ else:
+ file_trigger.write('0')
-def setup_real():
- file_trigger = open(os.path.join(led_path, 'trigger'), 'w')
- file_trigger.write('none')
file_trigger.close()
-if os.path.exists('on_real_device'):
- CommandWrapper = CommandWrapperReal
- CommandWrapperMDns = CommandWrapperReal
- CloudCommandHandler = CloudCommandHandlerReal
- WifiHandler = WifiHandlerReal
- setup_real()
-else:
- CommandWrapper = CommandWrapperFake
- CommandWrapperMDns = CommandWrapperReal
- CloudCommandHandler = CloudCommandHandlerFake
- WifiHandler = WifiHandlerPassthrough
- setup_fake()
-
-class State:
- def __init__(self):
- self.oauth_storage_ = Storage('oauth_creds')
- self.clear()
- def clear(self):
- self.credentials_ = None
- self.has_credentials_ = False
- self.has_wifi_ = False
- self.ssid_ = ''
- self.password_ = ''
- self.device_id_ = ''
- def reset(self):
- self.clear()
- self.dump()
- def dump(self):
- json_obj = {
- 'has_credentials': self.has_credentials_,
- 'has_wifi': self.has_wifi_,
- 'ssid': self.ssid_,
- 'password': self.password_,
- 'device_id': self.device_id_ }
- statefile = open('device_state.json', 'w')
- json.dump(json_obj, statefile)
- statefile.close()
-
- if self.has_credentials_:
- self.oauth_storage_.put(self.credentials_)
- def load(self):
- if os.path.exists('device_state.json'):
- statefile = open('device_state.json', 'r')
- json_obj = json.load(statefile)
- statefile.close()
-
- self.has_credentials_ = json_obj['has_credentials']
- self.has_wifi_ = json_obj['has_wifi']
- self.ssid_ = json_obj['ssid']
- self.password_ = json_obj['password']
- self.device_id_ = json_obj['device_id']
-
- if self.has_credentials_:
- self.credentials_ = self.oauth_storage_.get()
- def set_credentials(self, credentials, device_id):
- self.device_id_ = device_id
- self.credentials_ = credentials
- self.has_credentials_ = True
- self.dump()
- def set_wifi(self, ssid, password):
- self.ssid_ = ssid
- self.password_ = password
- self.has_wifi_ = True
- self.dump()
- def has_wifi(self):
- return self.has_wifi_
- def has_credentials(self):
- return self.has_credentials_
- def credentials(self):
- return self.credentials_
- def ssid(self):
- return self.ssid_
- def password(self):
- return self.password_
- def device_id(self):
- return self.device_id_
-
-def run_command(cmd):
- wrapper = CommandWrapper(cmd)
+ self.ioloop.add_timeout(datetime.timedelta(milliseconds=500),
+ lambda: self.set_led(times - 1, not value))
+
+
+class WifiHandler(object):
+ """Base class for wifi handlers."""
+
+ class Delegate(object):
+
+ def on_wifi_connected(self, unused_token):
+ """Token is optional, and all delegates should support it being None."""
+ raise Exception('Unhandled condition: WiFi connected')
+
+ def __init__(self, ioloop, state, delegate):
+ self.ioloop = ioloop
+ self.state = state
+ self.delegate = delegate
+
+ def start(self):
+ raise Exception('Start not implemented!')
+
+ def get_ssid(self):
+ raise Exception('Get SSID not implemented!')
+
+
+class WifiHandlerReal(WifiHandler):
+ """Real wifi handler.
+
+ Note that by using CommandWrapperFake, you can run WifiHandlerReal on fake
+ devices for testing the wifi-specific logic.
+ """
+
+ def __init__(self, ioloop, state, delegate):
+ super(WifiHandlerReal, self).__init__(ioloop, state, delegate)
+
+ self.command_wrapper = CommandWrapperReal
+ self.hostapd = self.CommandWrapper(hostapd_cmd)
+ self.wpa_supplicant = self.CommandWrapper(wpa_supplicant_cmd)
+ self.dhcpd = self.CommandWrapper(dhcpd_cmd)
+
+ def start(self):
+ if self.state.has_wifi():
+ self.switch_to_wifi(self.state.ssid(), self.state.password(), None)
+ else:
+ self.start_hostapd()
+
+ def start_hostapd(self):
+ self.hostapd.start()
+ time.sleep(3)
+ self.run_command(ifconfig_cmd)
+ self.dhcpd.start()
+
+ def switch_to_wifi(self, ssid, passwd, token):
+ try:
+ wpa_config = open(wpa_supplicant_conf, 'w')
+ wpa_config.write(wpa_supplicant_template % (ssid, passwd))
+ wpa_config.close()
+ self.hostapd.end()
+ self.dhcpd.end()
+ self.wpa_supplicant.start()
+ self.run_command(dhclient_release)
+ self.run_command(dhclient_renew)
+
+ self.state.set_wifi(ssid, passwd)
+ self.delegate.on_wifi_connected(token)
+ except DeviceUnregisteredError:
+ self.state.reset()
+ self.wpa_supplicant.end()
+ self.start_hostapd()
+
+ def stop(self):
+ self.hostapd.end()
+ self.wpa_supplicant.end()
+ self.dhcpd.end()
+
+ def get_ssid(self):
+ return self.state.get_ssid()
+
+ def run_command(self, cmd):
+ wrapper = self.command_wrapper(cmd)
wrapper.start()
wrapper.wait()
-class MDnsWrapper:
- def __init__(self):
- self.avahi_wrapper = None
- self.setup_name = None
- self.device_id = ''
- self.started = False
- def start(self):
- self.started = True
- self.run_command()
- def get_command(self):
- cmd = ['avahi-publish', '-s', 'Raspberry Pi' , '_privet._tcp', '8080',
- 'txtvers=2', 'type=wifi', 'ty=Raspberry Pi',
- 'id=' + self.device_id]
- if self.setup_name:
- cmd.append('setup=' + self.setup_name)
- return cmd
- def run_command(self):
- if self.avahi_wrapper:
- self.avahi_wrapper.end()
- self.avahi_wrapper.wait()
-
- self.avahi_wrapper = CommandWrapperMDns(self.get_command())
- self.avahi_wrapper.start()
- def set_id(self, device_id):
- self.device_id = device_id
- if self.started:
- self.run_command()
- def set_setup_name(self, setup_name):
- self.setup_name = setup_name
- if self.started:
- self.run_command()
-
-class CloudDevice:
- class Delegate:
- def on_device_started(self):
- raise Exception('Not implemented: Device started')
- def on_device_stopped(self):
- raise Exception('Not implemented: Device stopped')
- def __init__(self, ioloop, state, delegate):
- self.state = state
- self.http = httplib2.Http()
- if not os.path.isfile(_API_CLIENT_FILE):
- credentials = {
- 'oauth_client_id' : '',
- 'oauth_secret' : '',
- 'api_key' : ''
- }
- credentials_f = open(_API_CLIENT_FILE + '.samlpe', 'w')
- credentials_f.write(json.dumps(credentials));
- credentials_f.close()
- raise Exception('Missing ' + _API_CLIENT_FILE);
-
- credentials_f = open(_API_CLIENT_FILE)
- credentials = json.load(credentials_f)
- credentials_f.close()
-
- self.oauth_client_id = credentials['oauth_client_id']
- self.oauth_secret = credentials['oauth_secret']
- self.api_key = credentials['api_key']
-
- if not os.path.isfile(_API_DISCOVERY_FILE):
- raise Exception('Download https://developers.google.com/'
- 'cloud-devices/v1/discovery.json');
-
- f = open('discovery.json')
- discovery = f.read()
- f.close()
- self.gcd = build_from_document(
- discovery, developerKey=self.api_key, http=self.http)
-
- self.ioloop = ioloop
- self.active = True
- self.device_id = None
- self.credentials = None
- self.delegate = delegate
- self.command_handler = CloudCommandHandler(ioloop)
- def try_start(self, token): # Token may be null
- if self.state.has_credentials():
- self.credentials = self.state.credentials()
- self.device_id = self.state.device_id()
- self.run_device()
- elif token:
- self.register(token)
- else:
- print 'Device not registered and has no credentials.'
- print 'Waiting for registration.'
- def register(self, token):
- resource = {
- 'deviceDraft': DEVICE_DRAFT,
- 'oauthClientId': self.oauth_client_id
- }
- self.gcd.registrationTickets().patch(registrationTicketId=token,
- body=resource).execute()
+class WifiHandlerPassthrough(WifiHandler):
+ """Passthrough wifi handler."""
+
+ def __init__(self, ioloop, state, delegate):
+ super(WifiHandlerPassthrough, self).__init__(ioloop, state, delegate)
+
+ def start(self):
+ self.delegate.on_wifi_connected(None)
+
+ def switch_to_wifi(self, unused_ssid, unused_passwd, unused_token):
+ raise Exception('Should not be reached')
+
+ def get_ssid(self):
+ return 'dummy'
+
+
+class State(object):
+ """Device state."""
+
+ def __init__(self):
+ self.oauth_storage_ = Storage('oauth_creds')
+ self.clear()
+
+ def clear(self):
+ self.credentials_ = None
+ self.has_credentials_ = False
+ self.has_wifi_ = False
+ self.ssid_ = ''
+ self.password_ = ''
+ self.device_id_ = ''
+
+ def reset(self):
+ self.clear()
+ self.dump()
+
+ def dump(self):
+ """Saves device state to file."""
+ json_obj = {
+ 'has_credentials': self.has_credentials_,
+ 'has_wifi': self.has_wifi_,
+ 'ssid': self.ssid_,
+ 'password': self.password_,
+ 'device_id': self.device_id_
+ }
+ statefile = open(_DEVICE_STATE_FILE, 'w')
+ json.dump(json_obj, statefile)
+ statefile.close()
+
+ if self.has_credentials_:
+ self.oauth_storage_.put(self.credentials_)
+
+ def load(self):
+ if os.path.exists(_DEVICE_STATE_FILE):
+ statefile = open(_DEVICE_STATE_FILE, 'r')
+ json_obj = json.load(statefile)
+ statefile.close()
+
+ self.has_credentials_ = json_obj['has_credentials']
+ self.has_wifi_ = json_obj['has_wifi']
+ self.ssid_ = json_obj['ssid']
+ self.password_ = json_obj['password']
+ self.device_id_ = json_obj['device_id']
+
+ if self.has_credentials_:
+ self.credentials_ = self.oauth_storage_.get()
+
+ def set_credentials(self, credentials, device_id):
+ self.device_id_ = device_id
+ self.credentials_ = credentials
+ self.has_credentials_ = True
+ self.dump()
+
+ def set_wifi(self, ssid, password):
+ self.ssid_ = ssid
+ self.password_ = password
+ self.has_wifi_ = True
+ self.dump()
+
+ def has_wifi(self):
+ return self.has_wifi_
+
+ def has_credentials(self):
+ return self.has_credentials_
+
+ def credentials(self):
+ return self.credentials_
+
+ def ssid(self):
+ return self.ssid_
+
+ def password(self):
+ return self.password_
+
+ def device_id(self):
+ return self.device_id_
+
+
+class MDnsWrapper(object):
+ """Handles mDNS requests to device."""
+
+ def __init__(self, command_wrapper):
+ self.command_wrapper = command_wrapper
+ self.avahi_wrapper = None
+ self.setup_name = None
+ self.device_id = ''
+ self.started = False
+
+ def start(self):
+ self.started = True
+ self.run_command()
- finalTicket = self.gcd.registrationTickets().finalize(
- registrationTicketId=token).execute()
+ def get_command(self):
+ cmd = ['avahi-publish', '-s', 'Raspberry Pi', '_privet._tcp', '8080',
+ 'txtvers=2', 'type=wifi', 'ty=Raspberry Pi', 'id=' + self.device_id]
+ if self.setup_name:
+ cmd.append('setup=' + self.setup_name)
+ return cmd
- authorization_code = finalTicket['robotAccountAuthorizationCode']
- flow = OAuth2WebServerFlow(
- self.oauth_client_id, self.oauth_secret, _OAUTH_SCOPE,
- redirect_uri='oob')
- self.credentials = flow.step2_exchange(authorization_code)
- self.device_id = finalTicket['deviceDraft']['id']
- self.state.set_credentials(self.credentials, self.device_id)
- print 'Registered with device_id ', self.device_id
+ def run_command(self):
+ if self.avahi_wrapper:
+ self.avahi_wrapper.end()
+ self.avahi_wrapper.wait()
- self.run_device()
- def run_device(self):
- self.credentials.authorize(self.http)
+ self.avahi_wrapper = self.command_wrapper(self.get_command())
+ self.avahi_wrapper.start()
+ def set_id(self, device_id):
+ self.device_id = device_id
+ if self.started:
+ self.run_command()
+
+ def set_setup_name(self, setup_name):
+ self.setup_name = setup_name
+ if self.started:
+ self.run_command()
+
+
+class CloudDevice(object):
+ """Handles device registration and commands."""
+
+ class Delegate(object):
+
+ def on_device_started(self):
+ raise Exception('Not implemented: Device started')
+
+ def on_device_stopped(self):
+ raise Exception('Not implemented: Device stopped')
+
+ def __init__(self, ioloop, state, command_wrapper, delegate):
+ self.state = state
+ self.http = httplib2.Http()
+ if not os.path.isfile(_API_CLIENT_FILE):
+ credentials = {
+ 'oauth_client_id': '',
+ 'oauth_secret': '',
+ 'api_key': ''
+ }
+ credentials_f = open(_API_CLIENT_FILE + '.samlpe', 'w')
+ credentials_f.write(json.dumps(credentials))
+ credentials_f.close()
+ raise Exception('Missing ' + _API_CLIENT_FILE)
+
+ credentials_f = open(_API_CLIENT_FILE)
+ credentials = json.load(credentials_f)
+ credentials_f.close()
+
+ self.oauth_client_id = credentials['oauth_client_id']
+ self.oauth_secret = credentials['oauth_secret']
+ self.api_key = credentials['api_key']
+
+ if not os.path.isfile(_API_DISCOVERY_FILE):
+ raise Exception('Download https://developers.google.com/'
+ 'cloud-devices/v1/discovery.json')
+
+ f = open(_API_DISCOVERY_FILE)
+ discovery = f.read()
+ f.close()
+ self.gcd = build_from_document(discovery, developerKey=self.api_key,
+ http=self.http)
+
+ self.ioloop = ioloop
+ self.active = True
+ self.device_id = None
+ self.credentials = None
+ self.delegate = delegate
+ self.command_handler = command_wrapper(ioloop)
+
+ def try_start(self, token):
+ """Tries start or register device."""
+ if self.state.has_credentials():
+ self.credentials = self.state.credentials()
+ self.device_id = self.state.device_id()
+ self.run_device()
+ elif token:
+ self.register(token)
+ else:
+ print 'Device not registered and has no credentials.'
+ print 'Waiting for registration.'
+
+ def register(self, token):
+ """Register device."""
+ resource = {
+ 'deviceDraft': DEVICE_DRAFT,
+ 'oauthClientId': self.oauth_client_id
+ }
+
+ self.gcd.registrationTickets().patch(registration_ticke_id=token,
+ body=resource).execute()
+
+ final_ticket = self.gcd.registrationTickets().finalize(
+ registration_ticke_id=token).execute()
+
+ authorization_code = final_ticket['robotAccountAuthorizationCode']
+ flow = OAuth2WebServerFlow(self.oauth_client_id, self.oauth_secret,
+ _OAUTH_SCOPE, redirect_uri='oob')
+ self.credentials = flow.step2_exchange(authorization_code)
+ self.device_id = final_ticket['deviceDraft']['id']
+ self.state.set_credentials(self.credentials, self.device_id)
+ print 'Registered with device_id ', self.device_id
+
+ self.run_device()
+
+ def run_device(self):
+ """Runs device."""
+ self.credentials.authorize(self.http)
+
+ try:
+ self.gcd.devices().get(deviceId=self.device_id).execute()
+ except HttpError, e:
+ # Pretty good indication the device was deleted
+ if e.resp.status == 404:
+ raise DeviceUnregisteredError()
+ except AccessTokenRefreshError:
+ raise DeviceUnregisteredError()
+
+ self.check_commands()
+ self.delegate.on_device_started()
+
+ def check_commands(self):
+ """Checks device commands."""
+ if not self.active:
+ return
+ print 'Checking commands...'
+ commands = self.gcd.commands().list(deviceId=self.device_id,
+ state='queued').execute()
+
+ if 'commands' in commands:
+ print 'Found ', len(commands['commands']), ' commands'
+ vendor_command_name = None
+
+ for command in commands['commands']:
try:
- dev=self.gcd.devices().get(deviceId=self.device_id).execute()
- except HttpError, e:
- # Pretty good indication the device was deleted
- if e.resp.status == 404:
- raise DeviceUnregisteredError()
- except AccessTokenRefreshError:
- raise DeviceUnregisteredError()
-
- self.check_commands()
- self.delegate.on_device_started()
- def check_commands(self):
- if not self.active:
- return
-
- print 'Checking commands...'
-
- commands = self.gcd.commands().list(deviceId=self.device_id,
- state='queued').execute()
-
- if 'commands' in commands:
- print 'Found ', len(commands['commands']), ' commands'
- args = {}
- vendorCommandName = None
-
- for command in commands['commands']:
- try:
- if command['name'].startswith('base._'):
- vendorCommandName = command['name'][
- len('base._'):]
- if 'parameters' in command:
- parameters = command['parameters']
- else:
- parameters = {}
- else:
- vendorCommandName = None
- except KeyError:
- print 'Could not parse vendor command ',
- print repr(command)
- vendorCommandName = None
-
- if vendorCommandName:
- self.command_handler.handle_command(
- vendorCommandName,
- parameters)
-
- self.gcd.commands().patch(commandId = command['id'],
- body={'state': 'done'}).execute()
- else:
- print 'Found no commands'
-
- self.ioloop.add_timeout(datetime.timedelta(milliseconds=1000),
- self.check_commands)
- def stop(self):
- self.active = False
- def get_device_id(self):
- return self.device_id
+ if command['name'].startswith('base._'):
+ vendor_command_name = command['name'][len('base._'):]
+ if 'parameters' in command:
+ parameters = command['parameters']
+ else:
+ parameters = {}
+ else:
+ vendor_command_name = None
+ except KeyError:
+ print 'Could not parse vendor command ',
+ print repr(command)
+ vendor_command_name = None
+
+ if vendor_command_name:
+ self.command_handler.handle_command(vendor_command_name, parameters)
+
+ self.gcd.commands().patch(commandId=command['id'],
+ body={'state': 'done'}).execute()
+ else:
+ print 'Found no commands'
+
+ self.ioloop.add_timeout(datetime.timedelta(milliseconds=1000),
+ self.check_commands)
+
+ def stop(self):
+ self.active = False
+
+ def get_device_id(self):
+ return self.device_id
+
def get_only(f):
- def inner(self, request, response_func, *args):
- if request.method != 'GET':
- return False
- return f(self, request, response_func, *args)
- return inner
+ def inner(self, request, response_func, *args):
+ if request.method != 'GET':
+ return False
+ return f(self, request, response_func, *args)
+ return inner
+
def post_only(f):
- def inner(self, request, response_func, *args):
- if request.method != 'POST':
- return False
- return f(self, request, response_func, *args)
- return inner
+ def inner(self, request, response_func, *args):
+ # if request.method != 'POST':
+ # return False
+ return f(self, request, response_func, *args)
+ return inner
+
def wifi_provisioning(f):
- def inner(self, request, response_func, *args):
- if self.on_wifi:
- return False
- return f(self, request, response_func, *args)
- return inner
+ def inner(self, request, response_func, *args):
+ if self.on_wifi:
+ return False
+ return f(self, request, response_func, *args)
+ return inner
+
def post_provisioning(f):
- def inner(self, request, response_func, *args):
- if not self.on_wifi:
- return False
- return f(self, request, response_func, *args)
- return inner
+ def inner(self, request, response_func, *args):
+ if not self.on_wifi:
+ return False
+ return f(self, request, response_func, *args)
+ return inner
+
def extract_encryption_params(f):
- def inner(self, request, response_func, *args):
- try:
- client_id = request.headers['X-Privet-Client-ID']
- if 'X-Privet-Encrypted' in request.headers:
- encrypted = (request.headers['X-Privet-Encrypted'].lower()
- == 'true')
- else:
- encrypted = False
- except (KeyError, TypeError):
- print 'Missing client parameters in headers'
- response_func(400, { 'error': 'missing_client_parameters' })
- return True
+ """Extracts privet encription header and pass as parameter into function."""
+ def inner(self, request, response_func, *args):
+ """Extracts privet encription header."""
+ try:
+ client_id = request.headers['X-Privet-Client-ID']
+ if 'X-Privet-Encrypted' in request.headers:
+ encrypted = (request.headers['X-Privet-Encrypted'].lower() == 'true')
+ else:
+ encrypted = False
+ except (KeyError, TypeError):
+ print 'Missing client parameters in headers'
+ response_func(400, {'error': 'missing_client_parameters'})
+ return True
+
+ return f(self, request, response_func, client_id, encrypted, *args)
+ return inner
- return f(self, request, response_func, client_id, encrypted, *args)
- return inner
-def merge_dictionary(a, b):
- result = {}
- for k in a:
- result[k] = a[k]
- for k in b:
- result[k] = b[k]
- return result
+class WebRequestHandler(WifiHandler.Delegate, CloudDevice.Delegate):
+ """Handles HTTP requests."""
-class InvalidStepError(Exception):
+ class InvalidStepError(Exception):
pass
-class InvalidPackageError(Exception):
+ class InvalidPackageError(Exception):
pass
-class EncryptionError(Exception):
+ class EncryptionError(Exception):
pass
-class CancelableClosure:
+ class CancelableClosure(object):
+ """Allows to cancel callbacks."""
+
def __init__(self, function):
- self.function = function
+ self.function = function
+
def __call__(self):
- if self.function:
- return self.function
- return None
+ if self.function:
+ return self.function
+ return None
+
def cancel(self):
- self.function = None
+ self.function = None
+ class DummySession(object):
+ """Handles sessions."""
-class DummySession:
def __init__(self, client_id):
- self.client_id = client_id
- self.key = None
- def do_step(self, step, package):
- if step != 0:
- raise InvalidStepError()
+ self.client_id = client_id
+ self.key = None
+ def do_step(self, step, package):
+ if step != 0:
+ raise self.InvalidStepError()
+ self.key = package
+ return self.key
- self.key = package
- return self.key
def decrypt(self, cyphertext):
- return json.loads(cyphertext[len(self.key):])
+ return json.loads(cyphertext[len(self.key):])
+
def encrypt(self, plain_data):
- return self.key + json.dumps(plain_data)
+ return self.key + json.dumps(plain_data)
+
def get_client_id(self):
- return client_id
+ return self.client_id
+
def get_stype(self):
- return 'dummy'
+ return 'dummy'
+
+ def __init__(self, ioloop, state):
+ if os.path.exists('on_real_device'):
+ mdns_wrappers = CommandWrapperReal
+ cloud_wrapper = CloudCommandHandlerReal
+ wifi_handler = WifiHandlerReal
+ self.setup_real()
+ else:
+ mdns_wrappers = CommandWrapperReal
+ cloud_wrapper = CloudCommandHandlerFake
+ wifi_handler = WifiHandlerPassthrough
+ self.setup_fake()
+
+ self.cloud_device = CloudDevice(ioloop, state, cloud_wrapper, self)
+ self.wifi_handler = wifi_handler(ioloop, state, self)
+ self.mdns_wrapper = MDnsWrapper(mdns_wrappers)
+ self.on_wifi = False
+ self.registered = False
+ self.in_session = False
+ self.ioloop = ioloop
+ self.handlers = {
+ '/internal/ping': self.do_ping,
+ '/privet/info': self.do_info,
+ '/deprecated/wifi/switch': self.do_wifi_switch,
+ '/privet/v2/session/handshake': self.do_session_handshake,
+ '/privet/v2/session/cancel': self.do_session_cancel,
+ '/privet/v2/session/api': self.do_session_api,
+ '/privet/v2/setup/start':
+ self.get_insecure_api_handler(self.do_secure_setup_start),
+ '/privet/v2/setup/cancel':
+ self.get_insecure_api_handler(self.do_secure_setup_cancel),
+ '/privet/v2/setup/status':
+ self.get_insecure_api_handler(self.do_secure_status),
+ }
-class WebRequestHandler(WifiHandler.Delegate, CloudDevice.Delegate):
- def __init__(self, ioloop, state):
- self.cloud_device = CloudDevice(ioloop, state, self)
- self.wifi_handler = WifiHandler(ioloop, state, self)
- self.mdns_wrapper = MDnsWrapper()
- self.on_wifi = False
- self.registered = False
- self.in_session = False
- self.ioloop = ioloop
- self.handlers = {
- '/internal/ping': self.do_ping,
- '/privet/info': self.do_info,
- '/deprecated/wifi/switch': self.do_wifi_switch,
- '/privet/v2/session/handshake': self.do_session_handshake,
- '/privet/v2/session/cancel': self.do_session_cancel,
- '/privet/v2/session/api': self.do_session_api,
- '/privet/v2/setup/start': self.get_insecure_api_handler(
- self.do_secure_setup),
- '/privet/v2/setup/status': self.get_insecure_api_handler(
- self.do_secure_status),
+ self.current_session = None
+ self.session_cancel_callback = None
+ self.session_handlers = {
+ 'dummy': self.DummySession
+ }
- }
+ self.secure_handlers = {
+ '/privet/v2/setup/start': self.do_secure_setup_start,
+ '/privet/v2/setup/cancel': self.do_secure_setup_cancel,
+ '/privet/v2/setup/status': self.do_secure_status
+ }
- self.current_session = None
- self.session_cancel_callback = None
- self.session_handlers = {
- 'dummy' : DummySession
- }
+ @staticmethod
+ def setup_fake():
+ print 'Called setup'
- self.secure_handlers = {
- '/privet/v2/setup/start' : self.do_secure_setup,
- '/privet/v2/setup/status' : self.do_secure_status
- }
- def start(self):
- self.wifi_handler.start()
- self.mdns_wrapper.set_setup_name('RaspberryPi.camera.privet')
- self.mdns_wrapper.start()
- @get_only
- def do_ping(self, request, response_func):
- response_func(200, '{ "pong": true }')
- return True
- @get_only
- def do_public_info(self, request, response_func):
- info = merge_dictionary(
- self.get_common_info(),
- {
- 'stype' : self.session_handlers.keys()
- })
-
- self.real_send_response(
- request, 200, json.dumps(info))
- @post_provisioning
- @get_only
- def do_info(self, request, response_func):
- specific_info = {
- 'x-privet-token': 'sample',
- }
+ @staticmethod
+ def setup_real():
+ file_trigger = open(os.path.join(led_path, 'trigger'), 'w')
+ file_trigger.write('none')
+ file_trigger.close()
- info = merge_dictionary(
- self.get_common_info(),
- specific_info
- )
-
- self.real_send_response(
- request, 200, json.dumps(info))
- @post_only
- @wifi_provisioning
- def do_wifi_switch(self, request, response_func):
- data = json.loads(request.body)
- try:
- ssid = data['ssid']
- passw = data['passw']
- except KeyError:
- print 'Malformed content: ' + repr(data)
- self.real_send_response(
- request, 400, { 'error': 'invalid_params' })
- traceback.print_exc()
- return True
-
- response_func(200, { 'ssid': ssid } )
- self.wifi_handler.switch_to_wifi(ssid, passw, None)
- # TODO: Return to normal wifi after timeout (cancelable)
+ def start(self):
+ self.wifi_handler.start()
+ self.mdns_wrapper.set_setup_name('RaspberryPi.camera.privet')
+ self.mdns_wrapper.start()
+
+ @get_only
+ def do_ping(self, unused_request, response_func):
+ response_func(200, '{ "pong": true }')
+ return True
+
+ @get_only
+ def do_public_info(self, request, unused_response_func):
+ info = self.get_common_info().items() + {
+ 'stype': self.session_handlers.keys()}.items()
+ self.real_send_response(request, 200, json.dumps(info))
+
+ @post_provisioning
+ @get_only
+ def do_info(self, request, unused_response_func):
+ specific_info = {'x-privet-token': 'sample'}
+ info = self.get_common_info().items() + specific_info.items()
+ self.real_send_response(request, 200, json.dumps(info))
+
+ @post_only
+ @wifi_provisioning
+ def do_wifi_switch(self, request, response_func):
+ """Handles /deprecated/wifi/switch requests."""
+ data = json.loads(request.body)
+ try:
+ ssid = data['ssid']
+ passw = data['passw']
+ except KeyError:
+ print 'Malformed content: ' + repr(data)
+ self.real_send_response(request, 400, {'error': 'invalid_params'})
+ traceback.print_exc()
+ return True
+
+ response_func(200, {'ssid': ssid})
+ self.wifi_handler.switch_to_wifi(ssid, passw, None)
+ # TODO(noamsml): Return to normal wifi after timeout (cancelable)
+ return True
+
+ @extract_encryption_params
+ @post_only
+ @wifi_provisioning
+ def do_session_handshake(self, request, unused_response_func, client_id,
+ unused_encrypted):
+ """Handles /privet/v2/session/handshake requests."""
+ data = json.loads(request.body)
+ try:
+ stype = data['stype']
+ step = data['step']
+ package = base64.b64decode(data['package'])
+ except (KeyError, TypeError):
+ traceback.print_exc()
+ print 'Malformed content: ' + repr(data)
+ self.real_send_response(request, 400, {'error': 'invalid_params'})
+ return True
+
+ if self.current_session:
+ if client_id != self.current_session.get_client_id():
+ self.real_send_response(request, 500, {'error': 'in_session'})
return True
- @extract_encryption_params
- @post_only
- @wifi_provisioning
- def do_session_handshake(self, request, response_func, client_id,
- encrypted):
- data = json.loads(request.body)
- try:
- stype = data['stype']
- step = data['step']
- package = base64.b64decode(data['package'])
- except (KeyError, TypeError):
- traceback.print_exc()
- print 'Malformed content: ' + repr(data)
- self.real_send_response(
- request, 400, { 'error': 'invalid_params' })
- return True
-
- if self.current_session:
- if client_id != self.current_session.get_client_id():
- self.real_send_response(
- request, 500, { 'error': 'in_session' })
- return True
- if stype != self.current_session.get_stype():
- self.real_send_response(
- request, 500, { 'error': 'invalid_stype' })
- return True
- else:
- if stype not in self.session_handlers:
- self.real_send_response(
- request, 500, { 'error': 'invalid_stype' })
- return True
- self.current_session = self.session_handlers[stype](client_id)
-
- try:
- output_package = self.current_session.do_step(step, package)
- except InvalidStepError:
- self.real_send_response(
- request, 500, { 'error': 'invalid_step' })
- return True
- except InvalidPackageError:
- self.real_send_response(
- request, 500, { 'error': 'invalid_step' })
- return True
-
- return_obj = {
- 'stype' : stype,
- 'step' : step,
- 'package': base64.b64encode(output_package)}
-
- self.real_send_response(
- request, 200, json.dumps(return_obj))
-
- self.post_session_cancel()
+ if stype != self.current_session.get_stype():
+ self.real_send_response(request, 500, {'error': 'invalid_stype'})
return True
- @extract_encryption_params
- @post_only
- @wifi_provisioning
- def do_session_cancel(self, request, response_func, client_id,
- encrypted):
- if client_id == self.current_session.client_id:
- self.current_session = None
- if self.session_cancel_callback:
- self.session_cancel_callback.cancel()
- else:
- self.real_send_response(
- request, 400, { 'error': 'invalid_client_id' })
+ else:
+ if stype not in self.session_handlers:
+ self.real_send_response(request, 500, {'error': 'invalid_stype'})
return True
- @extract_encryption_params
- @post_only
- @wifi_provisioning
- def do_session_api(self, request, response_func, client_id, encrypted):
- if not encrypted:
- response_func(400, { 'error': 'encryption_required' })
- return True
-
- if (not self.current_session or
- client_id != self.current_session.client_id):
- response_func(405, { 'error': 'invalid_client_id' })
- return True
+ self.current_session = self.session_handlers[stype](client_id)
+
+ try:
+ output_package = self.current_session.do_step(step, package)
+ except self.InvalidStepError:
+ self.real_send_response(request, 500, {'error': 'invalid_step'})
+ return True
+ except self.InvalidPackageError:
+ self.real_send_response(request, 500, {'error': 'invalid_step'})
+ return True
+
+ return_obj = {
+ 'stype': stype,
+ 'step': step,
+ 'package': base64.b64encode(output_package)
+ }
+ self.real_send_response(request, 200, json.dumps(return_obj))
+ self.post_session_cancel()
+ return True
+
+ @extract_encryption_params
+ @post_only
+ @wifi_provisioning
+ def do_session_cancel(self, request, unused_response_func, client_id,
+ unused_encrypted):
+ if client_id == self.current_session.client_id:
+ self.current_session = None
+ if self.session_cancel_callback:
+ self.session_cancel_callback.cancel()
+ else:
+ self.real_send_response(request, 400, {'error': 'invalid_client_id'})
+ return True
+
+ @extract_encryption_params
+ @post_only
+ @wifi_provisioning
+ def do_session_api(self, request, response_func, client_id, encrypted):
+ """Handles /privet/v2/session/api requests."""
+ if not encrypted:
+ response_func(400, {'error': 'encryption_required'})
+ return True
+
+ if not self.current_session or client_id != self.current_session.client_id:
+ response_func(405, {'error': 'invalid_client_id'})
+ return True
+
+ try:
+ decrypted = self.current_session.decrypt(request.body)
+ except self.EncryptionError:
+ response_func(415, {'error': 'decryption_failed'})
+ return True
+
+ def encrypted_response_func(code, data):
+ if 'error' in data:
+ self.encrypted_send_response(request, code, data)
+ else:
+ self.encrypted_send_response(request, code, {
+ 'api': decrypted['api'],
+ 'response': data
+ })
+
+ if ('api' not in decrypted or 'request' not in decrypted or
+ type(decrypted['request']) != dict):
+ print 'Invalid params in API stage'
+ encrypted_response_func(400, {'error': 'invalid_params'})
+ return True
+
+ if decrypted['api'] in self.secure_handlers:
+ self.secure_handlers[decrypted['api']](request,
+ encrypted_response_func,
+ decrypted['request'])
+ else:
+ encrypted_response_func(400, {'error': 'unknown_api'})
+
+ self.post_session_cancel()
+ return True
+
+ def get_insecure_api_handler(self, handler):
+ def inner(request, func):
+ return self.insecure_api_handler(request, func, handler)
+ return inner
- try:
- decrypted = self.current_session.decrypt(request.body)
- except EncryptionError:
- response_func(415, { 'error': 'decryption_failed' })
- return True
-
- def encrypted_response_func(code, data):
- if 'error' in data:
- self.encrypted_send_response(request, code, data)
- else:
- self.encrypted_send_response(request, code, {
- 'api': decrypted['api'],
- 'response': data})
+ @post_only
+ def insecure_api_handler(self, request, response_func, handler):
+ real_params = json.loads(request.body) if request.body else {}
+ handler(request, response_func, real_params)
+ return True
+
+ def do_secure_status(self, unused_request, response_func, unused_params):
+ """Handles /privet/v2/setup/status requests."""
+ setup = {
+ 'registration': {
+ 'required': True
+ },
+ 'wifi': {
+ 'required': True
+ }
+ }
+ if self.on_wifi:
+ setup['wifi']['status'] = 'complete'
+ setup['wifi']['ssid'] = '' # TODO(noamsml): Add SSID to status
+ else:
+ setup['wifi']['status'] = 'available'
+
+ if self.cloud_device.get_device_id():
+ setup['registration']['status'] = 'complete'
+ setup['registration']['id'] = self.cloud_device.get_device_id()
+ else:
+ setup['registration']['status'] = 'available'
+ response_func(200, setup)
+
+ def do_secure_setup_start(self, unused_request, response_func, params):
+ """Handles /privet/v2/setup/start requests."""
+ has_wifi = False
+ token = None
+
+ try:
+ if 'wifi' in params:
+ has_wifi = True
+ ssid = params['wifi']['ssid']
+ passw = params['wifi']['passphrase']
+
+ if 'registration' in params:
+ token = params['registration']['ticketID']
+ except KeyError:
+ print 'Invalid params in bootstrap stage'
+ response_func(400, {'error': 'invalid_params'})
+ return
+
+ if has_wifi:
+ self.wifi_handler.switch_to_wifi(ssid, passw, token)
+ elif token:
+ self.cloud_device.register(token)
+ else:
+ response_func(400, {'error': 'invalid_params'})
+ return
+ self.do_secure_status(unused_request, response_func, params)
+
+ def do_secure_setup_cancel(self, request, response_func, params):
+ pass
- if ('api' not in decrypted or 'request' not in decrypted
- or type(decrypted['request']) != dict):
- print 'Invalid params in API stage'
- encrypted_response_func(400, { 'error': 'invalid_params' })
- return True
+ def handle_request(self, request):
+ def response_func(code, data):
+ self.real_send_response(request, code, json.dumps(data))
+
+ handled = False
+ if request.path in self.handlers:
+ handled = self.handlers[request.path](request, response_func)
+
+ if not handled:
+ self.real_send_response(request, 404, {'error': 'Not found'})
+
+ def encrypted_send_response(self, request, code, data):
+ self.real_send_response(request, code,
+ self.current_session.encrypt(data))
+
+ def real_send_response(self, request, code, data):
+ request.write('HTTP/1.1 %d Maybe OK\n' % code)
+ request.write('Content-Type: application/json\n')
+ request.write('Content-Length: %d\n' % len(data))
+ write_data = '\n%s' % data
+ request.write(str(write_data))
+ request.finish()
+
+ def device_state(self):
+ return 'idle'
+
+ def get_common_info(self):
+ return {
+ 'version': '2.0',
+ 'name': 'Sample Device',
+ 'device_state': self.device_state()
+ }
+ def post_session_cancel(self):
+ if self.session_cancel_callback:
+ self.session_cancel_callback.cancel()
+ self.session_cancel_callback = self.CancelableClosure(self.session_cancel)
+ self.ioloop.add_timeout(datetime.timedelta(minutes=2),
+ self.session_cancel_callback)
- if decrypted['api'] in self.secure_handlers:
- self.secure_handlers[decrypted['api']](request,
- encrypted_response_func,
- decrypted['request'])
- else:
- encrypted_response_func(400, { 'error': 'unknown_api' })
+ def session_cancel(self):
+ self.current_session = None
- self.post_session_cancel()
- return True
- def get_insecure_api_handler(self, handler):
- return lambda request, response_func: self.insecure_api_handler(
- request, response_func, handler)
- @post_only
- def insecure_api_handler(self, request, response_func, handler):
- real_params = json.loads(request.body)
- handler(request, response_func, real_params)
- return True
- def do_secure_setup(self, request, response_func, params):
- setup_handlers = {
- 'start': self.do_setup_start,
- 'cancel': self.do_setup_cancel }
-
- if not 'action' in params:
- response_func(400, { 'error': 'invalid_params' })
- return
-
- if params['action'] not in setup:
- response_func(400, { 'error': 'invalid_action' })
- return
-
- setup[params['action']](request, response_func, params)
- def do_secure_status(self, request, response_func, params):
- setup = {
- 'registration' : {
- 'required' : True
- },
- 'wifi' : {
- 'required' : True
- }
- }
- if self.on_wifi:
- setup['wifi']['status'] = 'complete'
- setup['wifi']['ssid'] = '' # TODO(noamsml): Add SSID to status
- else:
- setup['wifi']['status'] = 'available'
-
- if self.cloud_device.get_device_id():
- setup['registration']['status'] = 'complete'
- setup['registration']['id'] = self.cloud_device.get_device_id()
- else:
- specific_info['setup']['registration'] = 'available'
-
- def do_setup_start(self, request, response_func, params):
- has_wifi = False
- token = None
+ # WifiHandler.Delegate implementation
+ def on_wifi_connected(self, token):
+ self.mdns_wrapper.set_setup_name(None)
+ self.cloud_device.try_start(token)
+ self.on_wifi = True
- try:
- if 'wifi' in params:
- has_wifi = True
- ssid = params['wifi']['ssid']
- passw = params['wifi']['passphrase']
+ def on_device_started(self):
+ self.mdns_wrapper.set_id(self.cloud_device.get_device_id())
- if 'registration' in params:
- token = params['registration']['ticketID']
- except KeyError:
- print 'Invalid params in bootstrap stage'
- response_func(400, { 'error': 'invalid_params' })
- return
-
- response_func(200, { 'ssid' : ssid })
- if has_wifi:
- self.wifi_handler.switch_to_wifi(ssid, passw, token)
- elif token:
- self.cloud_device.register(token)
- else:
- response_func(400, { 'error': 'invalid_params' })
- def do_setup_cancel(self, request, response_func, params):
- pass
- def handle_request(self, request):
- def response_func(code, data):
- self.real_send_response(request, code, json.dumps(data))
-
- handled = False
- if request.path in self.handlers:
- handled = self.handlers[request.path](request, response_func)
-
- if not handled:
- self.real_send_response(request, 404,
- { 'error': 'Not found' })
- def encrypted_send_response(self, request, code, json):
- self.real_send_response(request, code,
- self.current_session.encrypt(json))
- def real_send_response(self, request, code, data):
- request.write('HTTP/1.1 %d Maybe OK\n' % code)
- request.write('Content-Type: application/json\n')
- request.write('Content-Length: %d\n' % len(data))
- write_data = '\n%s' % data
- request.write(str(write_data));
-
- request.finish()
- def device_state(self):
- return 'idle'
- def get_common_info(self):
- return { 'version' : '2.0',
- 'name' : 'Sample Device',
- 'device_state' : self.device_state() }
- def post_session_cancel(self):
- if self.session_cancel_callback:
- self.session_cancel_callback.cancel()
- self.session_cancel_callback = CancelableClosure(self.session_cancel)
- self.ioloop.add_timeout(datetime.timedelta(minutes=2),
- self.session_cancel_callback)
- def session_cancel(self):
- self.current_session = None
- # WifiHandler.Delegate implementation
- def on_wifi_connected(self, token):
- self.mdns_wrapper.set_setup_name(None)
- self.cloud_device.try_start(token)
- self.on_wifi = True
- def on_device_started(self):
- self.mdns_wrapper.set_id(self.cloud_device.get_device_id())
- def on_device_stopped(self):
- pass
- def stop(self):
- self.wifi_handler.stop()
- self.cloud_device.stop()
+ def on_device_stopped(self):
+ pass
-state = State()
-state.load()
+ def stop(self):
+ self.wifi_handler.stop()
+ self.cloud_device.stop()
-ioloop = IOLoop.instance()
+def main():
+ state = State()
+ state.load()
-handler = WebRequestHandler(ioloop, state)
-handler.start()
+ ioloop = IOLoop.instance()
-def logic_stop():
+ handler = WebRequestHandler(ioloop, state)
+ handler.start()
+ def logic_stop():
handler.stop()
+ atexit.register(logic_stop)
+ server = HTTPServer(handler.handle_request)
+ server.listen(8080)
-atexit.register(logic_stop)
+ ioloop.start()
-server = HTTPServer(handler.handle_request)
-server.listen(8080)
-ioloop.start()
+if __name__ == '__main__':
+ main()