aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ParrotZik.py172
-rwxr-xr-xParrotZikTray36
-rw-r--r--resource_manager.py135
-rw-r--r--setup.py2
4 files changed, 217 insertions, 128 deletions
diff --git a/ParrotZik.py b/ParrotZik.py
index d07f8b9..8e4b31b 100644
--- a/ParrotZik.py
+++ b/ParrotZik.py
@@ -1,11 +1,14 @@
import sys
+
+from resource_manager import GenericResourceManager
+from resource_manager import Version1ResourceManager
+from resource_manager import Version2ResourceManager
+
if sys.platform == "darwin":
import lightblue
else:
import bluetooth
-import ParrotProtocol
-from BeautifulSoup import BeautifulSoup
def connect(addr=None):
uuids = ["0ef0f502-f0ee-46c9-986c-54ed027807fb",
@@ -23,7 +26,7 @@ def connect(addr=None):
if len(service_matches) == 0:
print "Failed to find Parrot Zik RFCOMM service"
- return ParrotZikBase(ParrotZikApi(None))
+ return GenericResourceManager(None)
if sys.platform == "darwin":
first_match = service_matches[0]
@@ -47,64 +50,9 @@ def connect(addr=None):
sock.send('\x00\x03\x00')
data = sock.recv(1024)
- api = ParrotZikApi(sock)
- if api.version.startswith('1'):
- return ParrotZikVersion1(api)
- else:
- return ParrotZikVersion2(api)
-
-
-class ParrotZikApi(object):
- def __init__(self, socket):
- self.sock = socket
- self.notifications = []
-
- @property
- def version(self):
- answer = self.get("/api/software/version")
- try:
- return answer.software["version"]
- except KeyError:
- return answer.software['sip6']
-
- def get(self, resource):
- message = ParrotProtocol.getRequest(resource + '/get')
- return self.send_message(message)
-
- def toggle_on(self, resource):
- message = ParrotProtocol.getRequest(resource + '/enable')
- return self.send_message(message)
-
- def toggle_off(self, resource):
- message = ParrotProtocol.getRequest(resource + '/disable')
- return self.send_message(message)
-
- def set(self, resource, arg):
- message = ParrotProtocol.setRequest(resource + '/set', str(arg).lower())
- return self.send_message(message)
-
- def send_message(self, message):
- try:
- self.sock.send(str(message))
- except Exception:
- self.sock = ""
- return
- if sys.platform == "darwin":
- self.sock.recv(30)
- else:
- self.sock.recv(7)
+ return GenericResourceManager(sock)
- data = BeautifulSoup(self.sock.recv(1024))
- while not data.answer:
- if data.notify:
- self.notifications.append(data.notify)
- else:
- raise AssertionError('Unknown response')
- data = BeautifulSoup(self.sock.recv(1024))
- return data.answer
- def close(self):
- self.sock.close()
class BatteryStates:
CHARGED = 'charged'
@@ -152,40 +100,43 @@ class NoiseControlTypes:
class ParrotZikBase(object):
- def __init__(self, api):
- self.api = api
+ def __init__(self, resource_manager):
+ self.resource_manager = resource_manager
@property
def version(self):
- return self.api.version
+ return self.resource_manager.api_version
+
+ def refresh_battery(self):
+ self.resource_manager.fetch('/api/system/battery')
@property
def battery_state(self):
- answer = self.api.get("/api/system/battery")
+ answer = self.resource_manager.get("/api/system/battery")
return answer.system.battery["state"]
def get_battery_level(self, field_name):
- answer = self.api.get("/api/system/battery")
+ answer = self.resource_manager.get("/api/system/battery")
return int(answer.system.battery[field_name])
@property
def friendly_name(self):
- answer = self.api.get("/api/bluetooth/friendlyname")
+ answer = self.resource_manager.get("/api/bluetooth/friendlyname")
return answer.bluetooth["friendlyname"]
@property
def auto_connect(self):
- answer = self.api.get("/api/system/auto_connection/enabled")
+ answer = self.resource_manager.get("/api/system/auto_connection/enabled")
return self._result_to_bool(
answer.system.auto_connection["enabled"])
@auto_connect.setter
def auto_connect(self, arg):
- self.api.set("/api/system/auto_connection/enabled", arg)
+ self.resource_manager.set("/api/system/auto_connection/enabled", arg)
@property
def anc_phone_mode(self):
- answer = self.api.get("/api/system/anc_phone_mode/enabled")
+ answer = self.resource_manager.get("/api/system/anc_phone_mode/enabled")
return self._result_to_bool(
answer.system.anc_phone_mode["enabled"])
@@ -199,125 +150,118 @@ class ParrotZikBase(object):
class ParrotZikVersion1(ParrotZikBase):
- '''
- Known resources:
- * /api/software/version
- * /api/system/battery
- * /api/bluetooth/friendlyname
- * /api/system/auto_connection/enabled
- * /api/system/anc_phone_mode/enabled
- * /api/audio/specific_mode/enabled
- * /api/audio/sound_effect/enabled
- * /api/audio/noise_cancellation/enabled
- '''
+ def __init__(self, resource_manager):
+ super(ParrotZikVersion1, self).__init__(
+ resource_manager.get_resource_manager(
+ Version1ResourceManager))
+
+ @property
+ def version(self):
+ answer = self.resource_manager.get('/api/software/version')
+ return answer.software['version']
+
@property
def battery_level(self):
return int(self.get_battery_level('level'))
@property
def lou_reed_mode(self):
- answer = self.api.get("/api/audio/specific_mode/enabled")
+ answer = self.resource_manager.get("/api/audio/specific_mode/enabled")
return self._result_to_bool(
answer.audio.specific_mode["enabled"])
@lou_reed_mode.setter
def lou_reed_mode(self, arg):
- self.api.get("/api/audio/specific_mode/enabled", arg)
+ self.resource_manager.get("/api/audio/specific_mode/enabled", arg)
@property
def concert_hall(self):
- answer = self.api.get("/api/audio/sound_effect/enabled")
+ answer = self.resource_manager.get("/api/audio/sound_effect/enabled")
return self._result_to_bool(
answer.audio.sound_effect["enabled"])
@concert_hall.setter
def concert_hall(self, arg):
- self.api.get("/api/audio/sound_effect/enabled", arg)
+ self.resource_manager.get("/api/audio/sound_effect/enabled", arg)
@property
def cancel_noise(self):
- answer = self.api.get("/api/audio/noise_cancellation/enabled")
+ answer = self.resource_manager.get("/api/audio/noise_cancellation/enabled")
return self._result_to_bool(
answer.audio.noise_cancellation["enabled"])
@cancel_noise.setter
def cancel_noise(self, arg):
- self.api.set("/api/audio/noise_cancellation/enabled", arg)
+ self.resource_manager.set("/api/audio/noise_cancellation/enabled", arg)
class ParrotZikVersion2(ParrotZikBase):
- '''
- Known resources:
- * /api/software/version
- * /api/system/battery
- * /api/system/pi
- * /api/bluetooth/friendlyname
- * /api/system/auto_connection/enabled
- * /api/system/anc_phone_mode/enabled
- * /api/flight_mode
- * /api/sound_effect/enabled
- * /api/sound_effect/room_size
- * /api/sound_effect/angle
- * /api/audio/noise
- * /api/audio/noise_control
- * /api/audio/noise_control/enabled
- '''
+ def __init__(self, resource_manager):
+ super(ParrotZikVersion2, self).__init__(
+ resource_manager.get_resource_manager(
+ Version2ResourceManager))
+
+ @property
+ def version(self):
+ answer = self.resource_manager.get('/api/software/version')
+ return answer.software['sip6']
+
@property
def battery_level(self):
return self.get_battery_level('percent')
@property
def flight_mode(self):
- answer = self.api.get('/api/flight_mode')
+ answer = self.resource_manager.get('/api/flight_mode')
return self._result_to_bool(answer.flight_mode['enabled'])
@flight_mode.setter
def flight_mode(self, arg):
if arg:
- self.api.toggle_on('/api/flight_mode')
+ self.resource_manager.toggle_on('/api/flight_mode')
else:
- self.api.toggle_off('/api/flight_mode')
+ self.resource_manager.toggle_off('/api/flight_mode')
@property
def sound_effect(self):
- answer = self.api.get('/api/audio/sound_effect/enabled')
+ answer = self.resource_manager.get('/api/audio/sound_effect/enabled')
return self._result_to_bool(answer.audio.sound_effect['enabled'])
@sound_effect.setter
def sound_effect(self, arg):
- self.api.set('/api/audio/sound_effect/enabled', arg)
+ self.resource_manager.set('/api/audio/sound_effect/enabled', arg)
@property
def room(self):
- answer = self.api.get('/api/audio/sound_effect/room_size')
+ answer = self.resource_manager.get('/api/audio/sound_effect/room_size')
return answer.audio.sound_effect['room_size']
@room.setter
def room(self, arg):
- self.api.set('/api/audio/sound_effect/room_size', arg)
+ self.resource_manager.set('/api/audio/sound_effect/room_size', arg)
@property
def external_noise(self):
- answer = self.api.get('/api/audio/noise')
+ answer = self.resource_manager.get('/api/audio/noise')
return int(answer.audio.noise['external'])
@property
def internal_noise(self):
- answer = self.api.get('/api/audio/noise')
+ answer = self.resource_manager.get('/api/audio/noise')
return int(answer.audio.noise['internal'])
@property
def angle(self):
- answer = self.api.get('/api/audio/sound_effect/angle')
+ answer = self.resource_manager.get('/api/audio/sound_effect/angle')
return int(answer.audio.sound_effect['angle'])
@angle.setter
def angle(self, arg):
- self.api.set('/api/audio/sound_effect/angle', arg)
+ self.resource_manager.set('/api/audio/sound_effect/angle', arg)
@property
def noise_control(self):
- answer = self.api.get('/api/audio/noise_control')
+ answer = self.resource_manager.get('/api/audio/noise_control')
return NoiseControl.from_noise_control(answer.audio.noise_control)
@noise_control.setter
@@ -326,5 +270,5 @@ class ParrotZikVersion2(ParrotZikBase):
@property
def noise_control_enabled(self):
- answer = self.api.get('/api/audio/noise_control/enabled')
+ answer = self.resource_manager.get('/api/audio/noise_control/enabled')
return self._result_to_bool(answer.audio.noise_control['enabled'])
diff --git a/ParrotZikTray b/ParrotZikTray
index 373e9ee..6850a3f 100755
--- a/ParrotZikTray
+++ b/ParrotZikTray
@@ -5,6 +5,8 @@ import gtk
import BluetoothPairedDevices
from ParrotZik import BatteryStates
+from ParrotZik import ParrotZikVersion1
+from ParrotZik import ParrotZikVersion2
from ParrotZik import NoiseControlTypes
from ParrotZik import connect
from ParrotZik import Rooms
@@ -68,12 +70,12 @@ class ParrotZikIndicator(SysIndicator):
mac = BluetoothPairedDevices.ParrotZikMac()
if mac:
self.info_item.set_label("Connecting")
- parrot = connect(mac)
- if parrot.api.sock:
- if parrot.version.startswith('1'):
- self.version_1_interface.activate(parrot)
+ resource_manager = connect(mac)
+ if resource_manager.sock:
+ if resource_manager.api_version.startswith('1'):
+ self.version_1_interface.activate(resource_manager)
else:
- self.version_2_interface.activate(parrot)
+ self.version_2_interface.activate(resource_manager)
self.autorefresh(self)
self.autorefresh.start(self, REFRESH_FREQUENCY)
self.reconnect.stop()
@@ -114,12 +116,12 @@ class ParrotZikBaseInterface(object):
@property
def connected(self):
if self.parrot:
- return self.parrot.api.sock
+ return self.parrot.resource_manager.sock
else:
return False
- def activate(self, parrot):
- self.parrot = parrot
+ def activate(self, resource_manager):
+ self.parrot = self.parrot_class(resource_manager)
self.read_battery()
self.indicator.info_item.set_label("Connected to: "
+ self.parrot.friendly_name)
@@ -134,6 +136,10 @@ class ParrotZikBaseInterface(object):
self.indicator.active_interface = self
self.indicator.menu.reposition()
+ @property
+ def parrot_class(self):
+ raise NotImplementedError
+
def deactivate(self):
self.parrot = None
self.battery_level.hide()
@@ -157,9 +163,9 @@ class ParrotZikBaseInterface(object):
def read_battery(self):
if self.connected:
+ self.parrot.refresh_battery()
battery_level = self.parrot.battery_level
battery_state = self.parrot.battery_state
-
if battery_state == BatteryStates.CHARGING:
self.indicator.setIcon("zik-battery-charging")
elif battery_level > 80:
@@ -182,6 +188,8 @@ class ParrotZikBaseInterface(object):
class ParrotZikVersion1Interface(ParrotZikBaseInterface):
+ parrot_class = ParrotZikVersion1
+
def __init__(self, indicator):
super(ParrotZikVersion1Interface, self).__init__(indicator)
self.noise_cancelation = MenuItem(
@@ -196,11 +204,11 @@ class ParrotZikVersion1Interface(ParrotZikBaseInterface):
self.indicator.menu.append(self.lou_reed_mode)
self.indicator.menu.append(self.concert_hall_mode)
- def activate(self, parrot):
+ def activate(self, resource_manager):
self.noise_cancelation.show()
self.lou_reed_mode.show()
self.concert_hall_mode.show()
- super(ParrotZikVersion1Interface, self).activate(parrot)
+ super(ParrotZikVersion1Interface, self).activate(resource_manager)
self.noise_cancelation.set_active(self.parrot.cancel_noise)
self.lou_reed_mode.set_active(self.parrot.lou_reed_mode)
@@ -238,6 +246,8 @@ class ParrotZikVersion1Interface(ParrotZikBaseInterface):
class ParrotZikVersion2Interface(ParrotZikBaseInterface):
+ parrot_class = ParrotZikVersion2
+
def __init__(self, indicator):
self.room_dirty = False
self.angle_dirty = False
@@ -329,11 +339,11 @@ class ParrotZikVersion2Interface(ParrotZikBaseInterface):
self.indicator.menu.append(self.noise_cancelation)
self.indicator.menu.append(self.flight_mode)
- def activate(self, parrot):
+ def activate(self, resource_manager):
self.noise_cancelation.show()
self.flight_mode.show()
self.room_sound_effect.show()
- super(ParrotZikVersion2Interface, self).activate(parrot)
+ super(ParrotZikVersion2Interface, self).activate(resource_manager)
self.read_noise_cancelation()
self.flight_mode.set_active(self.parrot.flight_mode)
self.read_sound_effect_room()
diff --git a/resource_manager.py b/resource_manager.py
new file mode 100644
index 0000000..1a6e224
--- /dev/null
+++ b/resource_manager.py
@@ -0,0 +1,135 @@
+import sys
+from BeautifulSoup import BeautifulSoup
+import ParrotProtocol
+
+
+class ResourceManagerBase(object):
+ resources = [
+ ]
+
+ def __init__(self, socket, resource_values=None):
+ self.sock = socket
+ self.resource_values = resource_values or {}
+
+ def get(self, resource):
+ try:
+ return self.resource_values[resource]
+ except KeyError:
+ return self.fetch(resource)
+
+ def fetch(self, resource):
+ assert resource in self.resources, 'Unknown resource {}'.format(resource)
+ message = ParrotProtocol.getRequest(resource + '/get')
+ result = self.send_message(message)
+ self.resource_values[resource] = result
+ return result
+
+ def toggle_on(self, resource):
+ assert resource in self.resources, 'Unknown resource {}'.format(resource)
+ message = ParrotProtocol.getRequest(resource + '/enable')
+ self.send_message(message)
+ self.fetch(resource)
+
+ def toggle_off(self, resource):
+ assert resource in self.resources, 'Unknown resource {}'.format(resource)
+ message = ParrotProtocol.getRequest(resource + '/disable')
+ self.send_message(message)
+ self.fetch(resource)
+
+ def set(self, resource, arg):
+ assert resource in self.resources, 'Unknown resource {}'.format(resource)
+ message = ParrotProtocol.setRequest(resource + '/set', str(arg).lower())
+ self.send_message(message)
+ self.fetch(resource)
+
+ def send_message(self, message):
+ try:
+ self.sock.send(str(message))
+ except Exception:
+ self.sock = ""
+ return
+ else:
+ return self.get_answer()
+
+ def get_answer(self):
+ data = self.receive_message()
+ while not data.answer:
+ if data.notify:
+ self.handle_notification(data.notify)
+ else:
+ raise AssertionError('Unknown response')
+ data = self.receive_message()
+ return data.answer
+
+ def handle_notification(self, notification):
+
+ self.fetch(notification['path'].rsplit('/', 1)[0].encode('utf-8'))
+
+ def receive_message(self):
+ if sys.platform == "darwin":
+ self.sock.recv(30)
+ else:
+ self.sock.recv(7)
+ return BeautifulSoup(self.sock.recv(1024))
+
+ def close(self):
+ self.sock.close()
+
+
+class GenericResourceManager(ResourceManagerBase):
+ resources = {
+ '/api/software/version',
+ }
+
+ def __init__(self, sock):
+ super(GenericResourceManager, self).__init__(sock)
+ self.notifications = []
+
+ def handle_notification(self, notification):
+ self.notifications.append(notification)
+
+ def get_resource_manager(self, resource_manager_class):
+ resource_manager = resource_manager_class(self.sock, self.resource_values)
+ for notitifaction in self.notifications:
+ resource_manager.handle_notification(notitifaction)
+ return resource_manager
+
+ @property
+ def api_version(self):
+ answer = self.get("/api/software/version")
+ try:
+ return answer.software["version"]
+ except KeyError:
+ return answer.software['sip6']
+
+
+class Version1ResourceManager(ResourceManagerBase):
+ resources = {
+ '/api/software/version',
+ '/api/system/battery',
+ '/api/bluetooth/friendlyname',
+ '/api/system/auto_connection/enabled',
+ '/api/system/anc_phone_mode/enabled',
+ '/api/audio/specific_mode/enabled',
+ '/api/audio/sound_effect/enabled',
+ '/api/audio/noise_cancellation/enabled',
+ }
+
+class Version2ResourceManager(ResourceManagerBase):
+ resources = {
+ '/api/software/version',
+ '/api/system/battery',
+ '/api/system/pi',
+ '/api/bluetooth/friendlyname',
+ '/api/system/auto_connection/enabled',
+ '/api/system/anc_phone_mode/enabled',
+ '/api/flight_mode',
+ '/api/audio/sound_effect/enabled',
+ '/api/audio/sound_effect/room_size',
+ '/api/audio/sound_effect/angle',
+ '/api/audio/noise',
+ '/api/audio/noise_control',
+ '/api/audio/noise_control/enabled',
+ '/api/audio/track/metadata',
+ }
+
diff --git a/setup.py b/setup.py
index b919e8f..de58f17 100644
--- a/setup.py
+++ b/setup.py
@@ -43,7 +43,7 @@ setup(
'BeautifulSoup', 'bluetooth'
],
- py_modules=['ParrotZik', 'ParrotProtocol'],
+ py_modules=['ParrotZik', 'ParrotProtocol', 'resource_manager'],
scripts=["ParrotZikTray"]
)