From ac531e0ca0b17902821966530629e434d0cdf9f7 Mon Sep 17 00:00:00 2001 From: Marek Siarkowicz Date: Mon, 15 Jun 2015 03:09:17 +0200 Subject: Make names more pythonic --- ParrotZik.py | 222 ------------------------- ParrotZikTray | 475 ------------------------------------------------------ StatusAppMac.py | 25 --- SysIndicator.py | 254 ----------------------------- indicator.py | 254 +++++++++++++++++++++++++++++ parrot_zik.py | 222 +++++++++++++++++++++++++ parrot_zik_tray | 475 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 6 +- status_app_mac.py | 25 +++ 9 files changed, 979 insertions(+), 979 deletions(-) delete mode 100644 ParrotZik.py delete mode 100755 ParrotZikTray delete mode 100644 StatusAppMac.py delete mode 100644 SysIndicator.py create mode 100644 indicator.py create mode 100644 parrot_zik.py create mode 100755 parrot_zik_tray create mode 100644 status_app_mac.py diff --git a/ParrotZik.py b/ParrotZik.py deleted file mode 100644 index 52414b5..0000000 --- a/ParrotZik.py +++ /dev/null @@ -1,222 +0,0 @@ -from resource_manager import Version1ResourceManager -from resource_manager import Version2ResourceManager - - -class BatteryStates: - CHARGED = 'charged' - IN_USE = 'in_use' - CHARGING = 'charging' - representation = { - CHARGED: 'Charged', - IN_USE: 'In Use', - CHARGING: 'Charging', - } - -class Rooms: - CONCERT_HALL = 'concert' - JAZZ_CLUB = 'jazz' - LIVING_ROOM = 'living' - SILENT_ROOM = 'silent' - representation = { - CONCERT_HALL: 'Concert Hall', - JAZZ_CLUB: 'Jazz Club', - LIVING_ROOM: 'Living Room', - SILENT_ROOM: 'Silent Room', - } - -class NoiseControl(object): - def __init__(self, type, value): - self.type = type - self.value = value - - @classmethod - def from_noise_control(cls, noise_control): - return cls(noise_control['type'], int(noise_control['value'])) - - def __eq__(self, other): - return self.type == other.type and self.value == other.value - - def __str__(self): - return '{}++{}'.format(self.type, self.value) - -class NoiseControlTypes: - NOISE_CONTROL_MAX = NoiseControl('anc', 2) - NOISE_CONTROL_ON = NoiseControl('anc', 1) - NOISE_CONTROL_OFF = NoiseControl('off', 1) - STREET_MODE = NoiseControl('aoc', 1) - STREET_MODE_MAX = NoiseControl('aoc', 2) - - -class ParrotZikBase(object): - def __init__(self, resource_manager): - self.resource_manager = resource_manager - - @property - def version(self): - return self.resource_manager.api_version - - def refresh_battery(self): - self.resource_manager.fetch('/api/system/battery') - - @property - def battery_state(self): - answer = self.resource_manager.get("/api/system/battery") - return answer.system.battery["state"] - - def get_battery_level(self, field_name): - answer = self.resource_manager.get("/api/system/battery") - return int(answer.system.battery[field_name]) - - @property - def friendly_name(self): - answer = self.resource_manager.get("/api/bluetooth/friendlyname") - return answer.bluetooth["friendlyname"] - - @property - def auto_connect(self): - 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.resource_manager.set("/api/system/auto_connection/enabled", arg) - - @property - def anc_phone_mode(self): - answer = self.resource_manager.get("/api/system/anc_phone_mode/enabled") - return self._result_to_bool( - answer.system.anc_phone_mode["enabled"]) - - def _result_to_bool(self, result): - if result == "true": - return True - elif result == "false": - return False - else: - raise AssertionError(result) - - -class ParrotZikVersion1(ParrotZikBase): - 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.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.resource_manager.get("/api/audio/specific_mode/enabled", arg) - - @property - def concert_hall(self): - 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.resource_manager.get("/api/audio/sound_effect/enabled", arg) - - @property - def cancel_noise(self): - 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.resource_manager.set("/api/audio/noise_cancellation/enabled", arg) - - -class ParrotZikVersion2(ParrotZikBase): - 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.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.resource_manager.toggle_on('/api/flight_mode') - else: - self.resource_manager.toggle_off('/api/flight_mode') - - @property - def sound_effect(self): - 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.resource_manager.set('/api/audio/sound_effect/enabled', arg) - - @property - def room(self): - 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.resource_manager.set('/api/audio/sound_effect/room_size', arg) - - @property - def external_noise(self): - answer = self.resource_manager.get('/api/audio/noise') - return int(answer.audio.noise['external']) - - @property - def internal_noise(self): - answer = self.resource_manager.get('/api/audio/noise') - return int(answer.audio.noise['internal']) - - @property - def angle(self): - answer = self.resource_manager.get('/api/audio/sound_effect/angle') - return int(answer.audio.sound_effect['angle']) - - @angle.setter - def angle(self, arg): - self.resource_manager.set('/api/audio/sound_effect/angle', arg) - - @property - def noise_control(self): - answer = self.resource_manager.get('/api/audio/noise_control') - return NoiseControl.from_noise_control(answer.audio.noise_control) - - @noise_control.setter - def noise_control(self, arg): - pass - - @property - def noise_control_enabled(self): - 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 deleted file mode 100755 index d5b113c..0000000 --- a/ParrotZikTray +++ /dev/null @@ -1,475 +0,0 @@ -#!/usr/bin/env python -import functools -from threading import Lock - -import gtk - -import bluetooth_paired_devices -from ParrotZik import BatteryStates -from ParrotZik import ParrotZikVersion1 -from ParrotZik import ParrotZikVersion2 -from ParrotZik import NoiseControlTypes -from bluetooth_paired_devices import connect -from ParrotZik import Rooms -from SysIndicator import MenuItem -from SysIndicator import Menu -from SysIndicator import SysIndicator -import resource_manager - -REFRESH_FREQUENCY = 30000 -RECONNECT_FREQUENCY = 5000 - - -class repeat(object): - def __init__(self, f): - self.f = f - self.id = None - self.lock = Lock() - - def __call__(self, cls): - self.f(cls) - - def start(self, cls, frequency): - self.lock.acquire() - if not self.id: - def run(): - self.f(cls) - return True - - self.id = gtk.timeout_add(frequency, run) - self.lock.release() - - def stop(self): - self.lock.acquire() - if self.id: - gtk.timeout_remove(self.id) - self.id = None - self.lock.release() - - -class ParrotZikIndicator(SysIndicator): - def __init__(self): - - self.menu = Menu() - - self.info_item = MenuItem("Parrot Zik Not connected", - None, sensitive=False) - self.menu.append(self.info_item) - - self.version_1_interface = ParrotZikVersion1Interface(self) - self.version_2_interface = ParrotZikVersion2Interface(self) - self.quit = MenuItem("Quit", gtk.main_quit, checkitem=True) - self.menu.append(self.quit) - - SysIndicator.__init__(self, icon="zik-audio-headset", menu=self.menu) - - self.active_interface = None - - @repeat - def reconnect(self): - if self.active_interface: - self.reconnect.stop() - else: - self.info("Trying to connect") - try: - manager = connect() - except bluetooth_paired_devices.BluetoothIsNotOn: - self.info("Bluetooth is turned off") - except bluetooth_paired_devices.DeviceNotConnected: - self.info("Parrot Zik Not connected") - except bluetooth_paired_devices.ConnectionFailure: - self.info("Failed to connect") - else: - if manager.api_version.startswith('1'): - interface = self.version_1_interface - else: - interface = self.version_2_interface - try: - interface.activate(manager) - except resource_manager.DeviceDisconnected: - interface.deactivate() - else: - self.autorefresh(self) - self.autorefresh.start(self, REFRESH_FREQUENCY) - self.reconnect.stop() - - def info(self, message): - self.info_item.set_label(message) - print(message) - - @repeat - def autorefresh(self): - if self.active_interface: - self.active_interface.refresh() - else: - self.reconnect.start(self, RECONNECT_FREQUENCY) - self.autorefresh.stop() - - def main(self): - self.reconnect.start(self, RECONNECT_FREQUENCY) - SysIndicator.main(self) - -class ParrotZikBaseInterface(object): - def __init__(self, indicator): - self.indicator = indicator - self.parrot = None - self.battery_level = MenuItem("Battery Level:", None, sensitive=False, - visible=False) - self.battery_state = MenuItem("Battery State:", None, sensitive=False, - visible=False) - self.firmware_version = MenuItem("Firmware Version:", None, - sensitive=False, visible=False) - self.auto_connection = MenuItem("Auto Connection", self.toggle_auto_connection, - checkitem=True, visible=False) - self.indicator.menu.append(self.battery_level) - self.indicator.menu.append(self.battery_state) - self.indicator.menu.append(self.firmware_version) - self.indicator.menu.append(self.auto_connection) - - def activate(self, manager): - self.parrot = self.parrot_class(manager) - self.read_battery() - self.indicator.info("Connected to: " + self.parrot.friendly_name) - self.firmware_version.set_label( - "Firmware version: " + self.parrot.version) - self.auto_connection.set_active(self.parrot.auto_connect) - self.battery_level.show() - self.battery_state.show() - self.firmware_version.show() - self.auto_connection.show() - 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() - self.battery_state.hide() - self.firmware_version.hide() - self.auto_connection.hide() - self.indicator.menu.reposition() - self.indicator.active_interface = None - self.indicator.setIcon("zik-audio-headset") - self.indicator.info('Lost Connection') - self.indicator.reconnect.start(self.indicator, RECONNECT_FREQUENCY) - - def toggle_auto_connection(self, widget): - try: - self.parrot.auto_connect = self.auto_connection.get_active() - self.auto_connection.set_active(self.parrot.auto_connect) - except resource_manager.DeviceDisconnected: - self.deactivate() - - def refresh(self): - self.read_battery() - - def read_battery(self): - try: - self.parrot.refresh_battery() - battery_level = self.parrot.battery_level - battery_state = self.parrot.battery_state - except resource_manager.DeviceDisconnected: - self.deactivate() - else: - if battery_state == BatteryStates.CHARGING: - self.indicator.setIcon("zik-battery-charging") - elif battery_level > 80: - self.indicator.setIcon("zik-battery-100") - elif battery_level > 60: - self.indicator.setIcon("zik-battery-080") - elif battery_level > 40: - self.indicator.setIcon("zik-battery-060") - elif battery_level > 20: - self.indicator.setIcon("zik-battery-040") - else: - self.indicator.setIcon("zik-battery-low") - - self.battery_state.set_label( - "State: " + BatteryStates.representation[battery_state]) - self.battery_level.set_label( - "Battery Level: " + str(battery_level)) - - -class ParrotZikVersion1Interface(ParrotZikBaseInterface): - parrot_class = ParrotZikVersion1 - - def __init__(self, indicator): - super(ParrotZikVersion1Interface, self).__init__(indicator) - self.noise_cancelation = MenuItem( - "Noise Cancellation", self.toggle_noise_cancelation, - checkitem=True, visible=False) - self.lou_reed_mode = MenuItem("Lou Reed Mode", self.toggle_lou_reed_mode, - checkitem=True, visible=False) - self.concert_hall_mode = MenuItem( - "Concert Hall Mode", self.toggle_parrot_concert_hall, - checkitem=True, visible=False) - self.indicator.menu.append(self.noise_cancelation) - self.indicator.menu.append(self.lou_reed_mode) - self.indicator.menu.append(self.concert_hall_mode) - - def activate(self, manager): - self.noise_cancelation.show() - self.lou_reed_mode.show() - self.concert_hall_mode.show() - super(ParrotZikVersion1Interface, self).activate(manager) - self.noise_cancelation.set_active(self.parrot.cancel_noise) - self.lou_reed_mode.set_active(self.parrot.lou_reed_mode) - self.concert_hall_mode.set_active(self.parrot.concert_hall) - - def deactivate(self): - self.noise_cancelation.hide() - self.lou_reed_mode.hide() - self.concert_hall_mode.hide() - super(ParrotZikVersion1Interface, self).deactivate() - - def toggle_noise_cancelation(self, widget): - try: - self.parrot.cancel_noise = self.noise_cancelation.get_active() - self.noise_cancelation.set_active(self.parrot.cancel_noise) - except resource_manager.DeviceDisconnected: - self.deactivate() - - def toggle_lou_reed_mode(self, widget): - try: - self.parrot.lou_reed_mode = self.lou_reed_mode.get_active() - self.lou_reed_mode.set_active(self.parrot.lou_reed_mode) - self.concert_hall_mode.set_active(self.parrot.concert_hall) - self.concert_hall_mode.set_sensitive( - not self.lou_reed_mode.get_active()) - except resource_manager.DeviceDisconnected: - self.deactivate() - - def toggle_parrot_concert_hall(self, widget): - try: - self.parrot.concert_hall = self.concert_hall_mode.get_active() - self.concert_hall_mode.set_active(self.parrot.concert_hall) - except resource_manager.DeviceDisconnected: - self.deactivate() - - -class ParrotZikVersion2Interface(ParrotZikBaseInterface): - parrot_class = ParrotZikVersion2 - - def __init__(self, indicator): - self.room_dirty = False - self.angle_dirty = False - self.noise_cancelation_dirty = False - super(ParrotZikVersion2Interface, self).__init__(indicator) - self.noise_cancelation = MenuItem("Noise Control", None, visible=False) - self.noise_cancelation_submenu = Menu() - self.noise_cancelation.set_submenu(self.noise_cancelation_submenu) - - self.noise_control_cancelation_max = MenuItem( - "Max Calcelation", functools.partial( - self.toggle_noise_cancelation, - NoiseControlTypes.NOISE_CONTROL_MAX), checkitem=True) - self.noise_control_cancelation_on = MenuItem( - "Normal Cancelation", functools.partial( - self.toggle_noise_cancelation, - NoiseControlTypes.NOISE_CONTROL_ON), checkitem=True) - self.noise_control_off = MenuItem( - "Off", functools.partial( - self.toggle_noise_cancelation, - NoiseControlTypes.NOISE_CONTROL_OFF), checkitem=True) - self.noise_control_street_mode = MenuItem( - "Street Mode", functools.partial( - self.toggle_noise_cancelation, - NoiseControlTypes.STREET_MODE), checkitem=True) - self.noise_control_street_mode_max = MenuItem( - "Street Mode Max", functools.partial( - self.toggle_noise_cancelation, - NoiseControlTypes.STREET_MODE_MAX), checkitem=True) - self.noise_cancelation_submenu.append(self.noise_control_cancelation_max) - self.noise_cancelation_submenu.append(self.noise_control_cancelation_on) - self.noise_cancelation_submenu.append(self.noise_control_off) - self.noise_cancelation_submenu.append(self.noise_control_street_mode) - self.noise_cancelation_submenu.append(self.noise_control_street_mode_max) - - self.room_sound_effect = MenuItem( - "Room Sound Effect", None, visible=False) - self.room_sound_effect_submenu = Menu() - self.room_sound_effect.set_submenu(self.room_sound_effect_submenu) - - self.room_sound_effect_enabled = MenuItem( - "Enabled", self.toggle_room_sound_effect, checkitem=True) - self.rooms = MenuItem("Rooms", None, checkitem=False) - self.angle = MenuItem("Angle", None, checkitem=False) - self.room_sound_effect_submenu.append(self.room_sound_effect_enabled) - self.room_sound_effect_submenu.append(self.rooms) - self.room_sound_effect_submenu.append(self.angle) - - self.rooms_submenu = Menu() - self.rooms.set_submenu(self.rooms_submenu) - - self.concert_hall_mode = MenuItem( - "Concert Hall", functools.partial(self.toggle_room, Rooms.CONCERT_HALL), checkitem=True) - self.jazz_mode = MenuItem( - "Jazz Club", functools.partial(self.toggle_room, Rooms.JAZZ_CLUB), checkitem=True) - self.living_mode = MenuItem( - "Living Room", functools.partial(self.toggle_room, Rooms.LIVING_ROOM), checkitem=True) - self.silent_mode = MenuItem( - "Silent Room", functools.partial(self.toggle_room, Rooms.SILENT_ROOM), checkitem=True) - self.rooms_submenu.append(self.concert_hall_mode) - self.rooms_submenu.append(self.jazz_mode) - self.rooms_submenu.append(self.living_mode) - self.rooms_submenu.append(self.silent_mode) - - self.angle_submenu = Menu() - self.angle.set_submenu(self.angle_submenu) - self.angle_30 = MenuItem( - "30", functools.partial(self.toggle_angle, 30), checkitem=True) - self.angle_60 = MenuItem( - "60", functools.partial(self.toggle_angle, 60), checkitem=True) - self.angle_90 = MenuItem( - "90", functools.partial(self.toggle_angle, 90), checkitem=True) - self.angle_120 = MenuItem( - "120", functools.partial(self.toggle_angle, 120), checkitem=True) - self.angle_150 = MenuItem( - "150", functools.partial(self.toggle_angle, 150), checkitem=True) - self.angle_180 = MenuItem( - "180", functools.partial(self.toggle_angle, 180), checkitem=True) - self.angle_submenu.append(self.angle_30) - self.angle_submenu.append(self.angle_60) - self.angle_submenu.append(self.angle_90) - self.angle_submenu.append(self.angle_120) - self.angle_submenu.append(self.angle_150) - self.angle_submenu.append(self.angle_180) - - self.flight_mode = MenuItem("Flight Mode", self.toggle_flight_mode, - checkitem=True, visible=False) - self.indicator.menu.append(self.room_sound_effect) - self.indicator.menu.append(self.noise_cancelation) - self.indicator.menu.append(self.flight_mode) - - def activate(self, manager): - self.noise_cancelation.show() - self.flight_mode.show() - self.room_sound_effect.show() - super(ParrotZikVersion2Interface, self).activate(manager) - self._read_noise_cancelation() - self.flight_mode.set_active(self.parrot.flight_mode) - self._read_sound_effect_room() - self._read_sound_effect_angle() - sound_effect = self.parrot.sound_effect - - self.room_sound_effect_enabled.set_active(sound_effect) - self.concert_hall_mode.set_sensitive(sound_effect) - self.jazz_mode.set_sensitive(sound_effect) - self.living_mode.set_sensitive(sound_effect) - self.silent_mode.set_sensitive(sound_effect) - - self.angle_30.set_sensitive(sound_effect) - self.angle_60.set_sensitive(sound_effect) - self.angle_90.set_sensitive(sound_effect) - self.angle_120.set_sensitive(sound_effect) - self.angle_150.set_sensitive(sound_effect) - self.angle_180.set_sensitive(sound_effect) - - def deactivate(self): - self.noise_cancelation.hide() - self.concert_hall_mode.hide() - self.flight_mode.hide() - self.room_sound_effect.hide() - super(ParrotZikVersion2Interface, self).deactivate() - - def toggle_flight_mode(self, widget): - try: - self.parrot.flight_mode = self.flight_mode.get_active() - self.flight_mode.set_active(self.parrot.flight_mode) - except resource_manager.DeviceDisconnected: - self.deactivate() - - def toggle_room(self, room, widget): - try: - if not self.room_dirty: - self.parrot.room = room - self.room_dirty = True - self._read_sound_effect_room() - self.room_dirty = False - except resource_manager.DeviceDisconnected: - self.deactivate() - - def _read_sound_effect_room(self): - active_room = self.parrot.room - room_to_menuitem_map = ( - (Rooms.CONCERT_HALL, self.concert_hall_mode), - (Rooms.JAZZ_CLUB, self.jazz_mode), - (Rooms.LIVING_ROOM, self.living_mode), - (Rooms.SILENT_ROOM, self.silent_mode), - ) - for room, menu_item in room_to_menuitem_map: - menu_item.set_active(room == active_room) - - def toggle_room_sound_effect(self, widget): - try: - self.parrot.sound_effect = self.room_sound_effect_enabled.get_active() - sound_effect = self.parrot.sound_effect - self.room_sound_effect_enabled.set_active(sound_effect) - self.concert_hall_mode.set_sensitive(sound_effect) - self.jazz_mode.set_sensitive(sound_effect) - self.living_mode.set_sensitive(sound_effect) - self.silent_mode.set_sensitive(sound_effect) - self.angle_30.set_sensitive(sound_effect) - self.angle_60.set_sensitive(sound_effect) - self.angle_90.set_sensitive(sound_effect) - self.angle_120.set_sensitive(sound_effect) - self.angle_150.set_sensitive(sound_effect) - self.angle_180.set_sensitive(sound_effect) - except resource_manager.DeviceDisconnected: - self.deactivate() - - def toggle_angle(self, angle, widget): - try: - if not self.angle_dirty: - self.parrot.angle = angle - self.angle_dirty = True - self._read_sound_effect_angle() - self.angle_dirty = False - except resource_manager.DeviceDisconnected: - self.deactivate() - - def _read_sound_effect_angle(self): - active_angle = self.parrot.angle - angle_to_menuitem_map = ( - (30, self.angle_30), - (60, self.angle_60), - (90, self.angle_90), - (120, self.angle_120), - (150, self.angle_150), - (180, self.angle_180), - ) - for angle, menu_item in angle_to_menuitem_map: - menu_item.set_active(angle == active_angle) - - def toggle_noise_cancelation(self, noise_calcelation, widget): - try: - if not self.noise_cancelation_dirty: - self.parrot.noise_control = noise_calcelation - self.noise_cancelation_dirty = True - self._read_noise_cancelation() - self.noise_cancelation_dirty = False - except resource_manager.DeviceDisconnected: - self.deactivate() - - def _read_noise_cancelation(self): - active_noise_control = self.parrot.noise_control - noise_control_to_menuitem_map = ( - (NoiseControlTypes.NOISE_CONTROL_MAX, self.noise_control_cancelation_max), - (NoiseControlTypes.NOISE_CONTROL_ON, self.noise_control_cancelation_on), - (NoiseControlTypes.NOISE_CONTROL_OFF, self.noise_control_off), - (NoiseControlTypes.STREET_MODE, self.noise_control_street_mode), - (NoiseControlTypes.STREET_MODE_MAX, self.noise_control_street_mode_max), - ) - for noise_control, menu_item in noise_control_to_menuitem_map: - menu_item.set_active(active_noise_control == noise_control) - - -if __name__ == "__main__": - try: - indicator = ParrotZikIndicator() - indicator.main() - except KeyboardInterrupt: - pass diff --git a/StatusAppMac.py b/StatusAppMac.py deleted file mode 100644 index dccffe4..0000000 --- a/StatusAppMac.py +++ /dev/null @@ -1,25 +0,0 @@ -from Foundation import * -from AppKit import * - -class StatusApp(NSApplication): - - def initMenu(self, menu): - statusbar = NSStatusBar.systemStatusBar() - self.statusitem = statusbar.statusItemWithLength_( - NSVariableStatusItemLength) - - self.mymenu = menu - #add menu to statusitem - self.statusitem.setMenu_(menu.menubarMenu) - self.statusitem.setToolTip_('Parrot Zik Indicator') - - def setIcon(self,icon,icon_directory): - self.icon = NSImage.alloc().initByReferencingFile_( - icon_directory + icon + '.png') - self.icon.setScalesWhenResized_(True) - self.icon.setSize_((20, 20)) - self.statusitem.setImage_(self.icon) - - def clicked_(self, notification): - self.mymenu.actions[notification._.title]() - NSLog('clicked!') diff --git a/SysIndicator.py b/SysIndicator.py deleted file mode 100644 index 6aa4686..0000000 --- a/SysIndicator.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env python - -import sys -import os -import tempfile - -if sys.platform == "linux2" or sys.platform == "win32": - import gtk -elif sys.platform == "darwin": - from Foundation import * - from AppKit import * - from PyObjCTools import AppHelper - from StatusAppMac import StatusApp - - -class BaseIndicator(object): - def __init__(self, icon, menu, statusicon): - self.menu = menu - self.statusicon = statusicon - self.setIcon(icon) - - def gtk_right_click_event(self, icon, button, time): - if not self.menu_shown: - self.menu_shown = True - self.menu.popup(None, None, gtk.status_icon_position_menu, - button, time, self.statusicon) - else: - self.menu_shown = False - self.menu.popdown() - - def setIcon(self, name): - raise NotImplementedError - - def main(self): - raise NotImplementedError - - def show_about_dialog(self, widget): - raise NotImplementedError - - -class WindowsIndicator(BaseIndicator): - def __init__(self, icon, menu): - self.icon_directory = ( - os.path.dirname(os.path.realpath(sys.argv[0])) - + os.path.sep + 'share' + os.path.sep + 'icons' - + os.path.sep +'zik' + os.path.sep) - self.menu_shown = False - sys.stdout = open(tempfile.gettempdir() - + os.path.sep + "zik_tray_stdout.log", "w") - sys.stderr = open(tempfile.gettempdir() - + os.path.sep + "zik_tray_stderr.log", "w") - statusicon = gtk.StatusIcon() - statusicon.connect("popup-menu", self.gtk_right_click_event) - statusicon.set_tooltip("Parrot Zik") - super(WindowsIndicator, self).__init__(icon, menu, statusicon) - - def setIcon(self, name): - self.statusicon.set_from_file(self.icon_directory + name + '.png') - - def main(self): - gtk.main() - - def show_about_dialog(self, widget): - about_dialog = gtk.AboutDialog() - about_dialog.set_destroy_with_parent(True) - about_dialog.set_name("Parrot Zik Tray") - about_dialog.set_version("0.3") - about_dialog.set_authors(["Dmitry Moiseev m0sia@m0sia.ru"]) - about_dialog.run() - about_dialog.destroy() - - -class LinuxIndicator(BaseIndicator): - def __init__(self, icon, menu): - import appindicator - self.icon_directory = (os.path.sep + 'usr' + os.path.sep + 'share' - + os.path.sep + 'icons' + os.path.sep+'zik' - + os.path.sep) - if not os.path.isdir(self.icon_directory): - self.icon_directory = (os.path.dirname(sys.argv[0]) - + os.path.sep + 'share' + os.path.sep - + 'icons' + os.path.sep+'zik' - + os.path.sep) - statusicon = appindicator.Indicator( - "new-parrotzik-indicator", "indicator-messages", - appindicator.CATEGORY_APPLICATION_STATUS) - statusicon.set_status(appindicator.STATUS_ACTIVE) - statusicon.set_icon_theme_path(self.icon_directory) - statusicon.set_menu(menu.gtk_menu) - super(LinuxIndicator, self).__init__(icon, menu, statusicon) - - def setIcon(self, name): - self.statusicon.set_icon(name) - - def main(self): - gtk.main() - - def show_about_dialog(self, widget): - about_dialog = gtk.AboutDialog() - about_dialog.set_destroy_with_parent(True) - about_dialog.set_name("Parrot Zik Tray") - about_dialog.set_version("0.3") - about_dialog.set_authors(["Dmitry Moiseev m0sia@m0sia.ru"]) - about_dialog.run() - about_dialog.destroy() - - -class DarwinIndicator(BaseIndicator): - def __init__(self, icon, menu): - self.icon_directory = ( - os.path.dirname(os.path.realpath(sys.argv[0])) + os.path.sep - + 'share' + os.path.sep + 'icons' + os.path.sep + 'zik' - + os.path.sep) - statusicon = StatusApp.sharedApplication() - statusicon.initMenu(menu) - super(DarwinIndicator, self).__init__(icon, menu, statusicon) - - def setIcon(self, name): - self.statusicon.setIcon(name, self.icon_directory) - - def main(self): - AppHelper.runEventLoop() - - def show_about_dialog(self, widget): - pass - - -class NSMenu(object): - def __init__(self): - self.actions = {} - self.menubarMenu = NSMenu.alloc().init() - self.menubarMenu.setAutoenablesItems_(False) - - def append(self, menu_item): - self.actions[menu_item.title] = menu_item.action - self.menubarMenu.addItem_(menu_item.nsmenu_item) - - def reposition(self): - # TODO - pass - -class GTKMenu(object): - def __init__(self): - self.gtk_menu = gtk.Menu() - - def append(self, menu_item): - self.gtk_menu.append(menu_item.base_item) - - def reposition(self): - self.gtk_menu.reposition() - - -class MenuItemBase(object): - def __init__(self, base_item, sensitive, visible): - self.base_item = base_item - self.set_sensitive(sensitive) - if visible: - self.show() - else: - self.hide() - - def set_sensitive(self, option): - raise NotImplementedError - - def set_active(self, option): - raise NotImplementedError - - def get_active(self): - raise NotImplementedError - - def set_label(self, option): - raise NotImplementedError - - def show(self): - self.base_item.show() - - def hide(self): - self.base_item.hide() - - def set_submenu(self, menu): - raise NotImplementedError - -class GTKMenuItem(MenuItemBase): - def __init__(self, name, action, sensitive=True, checkitem=False, visible=True): - if checkitem: - gtk_item = gtk.CheckMenuItem(name) - else: - gtk_item = gtk.MenuItem(name) - if action: - gtk_item.connect("activate", action) - super(GTKMenuItem, self).__init__(gtk_item, sensitive, visible) - - def set_sensitive(self, option): - return self.base_item.set_sensitive(option) - - def set_active(self, option): - return self.base_item.set_active(option) - - def get_active(self): - return self.base_item.get_active() - - def set_label(self, option): - return self.base_item.set_label(option) - - def set_submenu(self, menu): - self.base_item.set_submenu(menu.gtk_menu) - - -class NSMenuItem(MenuItemBase): - def __init__(self, name, action, sensitive=True, checkitem=False, visible=True): - self.title = name - self.action = action - nsmenu_item = ( - NSMenuItem.alloc().initWithTitle_action_keyEquivalent_( - name, 'clicked:', '')) - super(NSMenuItem, self).__init__(nsmenu_item, sensitive, visible) - - def set_sensitive(self, option): - self.base_item.setEnabled_(option) - - def set_active(self, option): - self.base_item.setState_(option) - - def get_active(self): - return self.base_item.state - - def set_label(self, option): - self.title = option - self.base_item.setTitle_(option) - -if sys.platform == 'linux2': - SysIndicator = LinuxIndicator - Menu = GTKMenu - MenuItem = GTKMenuItem -elif sys.platform == 'win32': - SysIndicator = WindowsIndicator - Menu = GTKMenu - MenuItem = GTKMenuItem -elif sys.platform == 'darwin': - SysIndicator = DarwinIndicator - Menu = NSMenu - MenuItem = NSMenuItem -else: - raise Exception('Platform not supported') - -if __name__ == "__main__": - - quit_item = MenuItem("Quit", sys.exit, True) - - menu = Menu() - menu.append(quit_item) - - indicator = SysIndicator(icon="zik-audio-headset", menu=menu) - indicator.main() diff --git a/indicator.py b/indicator.py new file mode 100644 index 0000000..05ffec7 --- /dev/null +++ b/indicator.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python + +import sys +import os +import tempfile + +if sys.platform == "linux2" or sys.platform == "win32": + import gtk +elif sys.platform == "darwin": + from Foundation import * + from AppKit import * + from PyObjCTools import AppHelper + from status_app_mac import StatusApp + + +class BaseIndicator(object): + def __init__(self, icon, menu, statusicon): + self.menu = menu + self.statusicon = statusicon + self.setIcon(icon) + + def gtk_right_click_event(self, icon, button, time): + if not self.menu_shown: + self.menu_shown = True + self.menu.popup(None, None, gtk.status_icon_position_menu, + button, time, self.statusicon) + else: + self.menu_shown = False + self.menu.popdown() + + def setIcon(self, name): + raise NotImplementedError + + def main(self): + raise NotImplementedError + + def show_about_dialog(self, widget): + raise NotImplementedError + + +class WindowsIndicator(BaseIndicator): + def __init__(self, icon, menu): + self.icon_directory = ( + os.path.dirname(os.path.realpath(sys.argv[0])) + + os.path.sep + 'share' + os.path.sep + 'icons' + + os.path.sep +'zik' + os.path.sep) + self.menu_shown = False + sys.stdout = open(tempfile.gettempdir() + + os.path.sep + "zik_tray_stdout.log", "w") + sys.stderr = open(tempfile.gettempdir() + + os.path.sep + "zik_tray_stderr.log", "w") + statusicon = gtk.StatusIcon() + statusicon.connect("popup-menu", self.gtk_right_click_event) + statusicon.set_tooltip("Parrot Zik") + super(WindowsIndicator, self).__init__(icon, menu, statusicon) + + def setIcon(self, name): + self.statusicon.set_from_file(self.icon_directory + name + '.png') + + def main(self): + gtk.main() + + def show_about_dialog(self, widget): + about_dialog = gtk.AboutDialog() + about_dialog.set_destroy_with_parent(True) + about_dialog.set_name("Parrot Zik Tray") + about_dialog.set_version("0.3") + about_dialog.set_authors(["Dmitry Moiseev m0sia@m0sia.ru"]) + about_dialog.run() + about_dialog.destroy() + + +class LinuxIndicator(BaseIndicator): + def __init__(self, icon, menu): + import appindicator + self.icon_directory = (os.path.sep + 'usr' + os.path.sep + 'share' + + os.path.sep + 'icons' + os.path.sep+'zik' + + os.path.sep) + if not os.path.isdir(self.icon_directory): + self.icon_directory = (os.path.dirname(sys.argv[0]) + + os.path.sep + 'share' + os.path.sep + + 'icons' + os.path.sep+'zik' + + os.path.sep) + statusicon = appindicator.Indicator( + "new-parrotzik-indicator", "indicator-messages", + appindicator.CATEGORY_APPLICATION_STATUS) + statusicon.set_status(appindicator.STATUS_ACTIVE) + statusicon.set_icon_theme_path(self.icon_directory) + statusicon.set_menu(menu.gtk_menu) + super(LinuxIndicator, self).__init__(icon, menu, statusicon) + + def setIcon(self, name): + self.statusicon.set_icon(name) + + def main(self): + gtk.main() + + def show_about_dialog(self, widget): + about_dialog = gtk.AboutDialog() + about_dialog.set_destroy_with_parent(True) + about_dialog.set_name("Parrot Zik Tray") + about_dialog.set_version("0.3") + about_dialog.set_authors(["Dmitry Moiseev m0sia@m0sia.ru"]) + about_dialog.run() + about_dialog.destroy() + + +class DarwinIndicator(BaseIndicator): + def __init__(self, icon, menu): + self.icon_directory = ( + os.path.dirname(os.path.realpath(sys.argv[0])) + os.path.sep + + 'share' + os.path.sep + 'icons' + os.path.sep + 'zik' + + os.path.sep) + statusicon = StatusApp.sharedApplication() + statusicon.initMenu(menu) + super(DarwinIndicator, self).__init__(icon, menu, statusicon) + + def setIcon(self, name): + self.statusicon.setIcon(name, self.icon_directory) + + def main(self): + AppHelper.runEventLoop() + + def show_about_dialog(self, widget): + pass + + +class NSMenu(object): + def __init__(self): + self.actions = {} + self.menubarMenu = NSMenu.alloc().init() + self.menubarMenu.setAutoenablesItems_(False) + + def append(self, menu_item): + self.actions[menu_item.title] = menu_item.action + self.menubarMenu.addItem_(menu_item.nsmenu_item) + + def reposition(self): + # TODO + pass + +class GTKMenu(object): + def __init__(self): + self.gtk_menu = gtk.Menu() + + def append(self, menu_item): + self.gtk_menu.append(menu_item.base_item) + + def reposition(self): + self.gtk_menu.reposition() + + +class MenuItemBase(object): + def __init__(self, base_item, sensitive, visible): + self.base_item = base_item + self.set_sensitive(sensitive) + if visible: + self.show() + else: + self.hide() + + def set_sensitive(self, option): + raise NotImplementedError + + def set_active(self, option): + raise NotImplementedError + + def get_active(self): + raise NotImplementedError + + def set_label(self, option): + raise NotImplementedError + + def show(self): + self.base_item.show() + + def hide(self): + self.base_item.hide() + + def set_submenu(self, menu): + raise NotImplementedError + +class GTKMenuItem(MenuItemBase): + def __init__(self, name, action, sensitive=True, checkitem=False, visible=True): + if checkitem: + gtk_item = gtk.CheckMenuItem(name) + else: + gtk_item = gtk.MenuItem(name) + if action: + gtk_item.connect("activate", action) + super(GTKMenuItem, self).__init__(gtk_item, sensitive, visible) + + def set_sensitive(self, option): + return self.base_item.set_sensitive(option) + + def set_active(self, option): + return self.base_item.set_active(option) + + def get_active(self): + return self.base_item.get_active() + + def set_label(self, option): + return self.base_item.set_label(option) + + def set_submenu(self, menu): + self.base_item.set_submenu(menu.gtk_menu) + + +class NSMenuItem(MenuItemBase): + def __init__(self, name, action, sensitive=True, checkitem=False, visible=True): + self.title = name + self.action = action + nsmenu_item = ( + NSMenuItem.alloc().initWithTitle_action_keyEquivalent_( + name, 'clicked:', '')) + super(NSMenuItem, self).__init__(nsmenu_item, sensitive, visible) + + def set_sensitive(self, option): + self.base_item.setEnabled_(option) + + def set_active(self, option): + self.base_item.setState_(option) + + def get_active(self): + return self.base_item.state + + def set_label(self, option): + self.title = option + self.base_item.setTitle_(option) + +if sys.platform == 'linux2': + SysIndicator = LinuxIndicator + Menu = GTKMenu + MenuItem = GTKMenuItem +elif sys.platform == 'win32': + SysIndicator = WindowsIndicator + Menu = GTKMenu + MenuItem = GTKMenuItem +elif sys.platform == 'darwin': + SysIndicator = DarwinIndicator + Menu = NSMenu + MenuItem = NSMenuItem +else: + raise Exception('Platform not supported') + +if __name__ == "__main__": + + quit_item = MenuItem("Quit", sys.exit, True) + + menu = Menu() + menu.append(quit_item) + + indicator = SysIndicator(icon="zik-audio-headset", menu=menu) + indicator.main() diff --git a/parrot_zik.py b/parrot_zik.py new file mode 100644 index 0000000..52414b5 --- /dev/null +++ b/parrot_zik.py @@ -0,0 +1,222 @@ +from resource_manager import Version1ResourceManager +from resource_manager import Version2ResourceManager + + +class BatteryStates: + CHARGED = 'charged' + IN_USE = 'in_use' + CHARGING = 'charging' + representation = { + CHARGED: 'Charged', + IN_USE: 'In Use', + CHARGING: 'Charging', + } + +class Rooms: + CONCERT_HALL = 'concert' + JAZZ_CLUB = 'jazz' + LIVING_ROOM = 'living' + SILENT_ROOM = 'silent' + representation = { + CONCERT_HALL: 'Concert Hall', + JAZZ_CLUB: 'Jazz Club', + LIVING_ROOM: 'Living Room', + SILENT_ROOM: 'Silent Room', + } + +class NoiseControl(object): + def __init__(self, type, value): + self.type = type + self.value = value + + @classmethod + def from_noise_control(cls, noise_control): + return cls(noise_control['type'], int(noise_control['value'])) + + def __eq__(self, other): + return self.type == other.type and self.value == other.value + + def __str__(self): + return '{}++{}'.format(self.type, self.value) + +class NoiseControlTypes: + NOISE_CONTROL_MAX = NoiseControl('anc', 2) + NOISE_CONTROL_ON = NoiseControl('anc', 1) + NOISE_CONTROL_OFF = NoiseControl('off', 1) + STREET_MODE = NoiseControl('aoc', 1) + STREET_MODE_MAX = NoiseControl('aoc', 2) + + +class ParrotZikBase(object): + def __init__(self, resource_manager): + self.resource_manager = resource_manager + + @property + def version(self): + return self.resource_manager.api_version + + def refresh_battery(self): + self.resource_manager.fetch('/api/system/battery') + + @property + def battery_state(self): + answer = self.resource_manager.get("/api/system/battery") + return answer.system.battery["state"] + + def get_battery_level(self, field_name): + answer = self.resource_manager.get("/api/system/battery") + return int(answer.system.battery[field_name]) + + @property + def friendly_name(self): + answer = self.resource_manager.get("/api/bluetooth/friendlyname") + return answer.bluetooth["friendlyname"] + + @property + def auto_connect(self): + 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.resource_manager.set("/api/system/auto_connection/enabled", arg) + + @property + def anc_phone_mode(self): + answer = self.resource_manager.get("/api/system/anc_phone_mode/enabled") + return self._result_to_bool( + answer.system.anc_phone_mode["enabled"]) + + def _result_to_bool(self, result): + if result == "true": + return True + elif result == "false": + return False + else: + raise AssertionError(result) + + +class ParrotZikVersion1(ParrotZikBase): + 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.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.resource_manager.get("/api/audio/specific_mode/enabled", arg) + + @property + def concert_hall(self): + 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.resource_manager.get("/api/audio/sound_effect/enabled", arg) + + @property + def cancel_noise(self): + 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.resource_manager.set("/api/audio/noise_cancellation/enabled", arg) + + +class ParrotZikVersion2(ParrotZikBase): + 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.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.resource_manager.toggle_on('/api/flight_mode') + else: + self.resource_manager.toggle_off('/api/flight_mode') + + @property + def sound_effect(self): + 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.resource_manager.set('/api/audio/sound_effect/enabled', arg) + + @property + def room(self): + 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.resource_manager.set('/api/audio/sound_effect/room_size', arg) + + @property + def external_noise(self): + answer = self.resource_manager.get('/api/audio/noise') + return int(answer.audio.noise['external']) + + @property + def internal_noise(self): + answer = self.resource_manager.get('/api/audio/noise') + return int(answer.audio.noise['internal']) + + @property + def angle(self): + answer = self.resource_manager.get('/api/audio/sound_effect/angle') + return int(answer.audio.sound_effect['angle']) + + @angle.setter + def angle(self, arg): + self.resource_manager.set('/api/audio/sound_effect/angle', arg) + + @property + def noise_control(self): + answer = self.resource_manager.get('/api/audio/noise_control') + return NoiseControl.from_noise_control(answer.audio.noise_control) + + @noise_control.setter + def noise_control(self, arg): + pass + + @property + def noise_control_enabled(self): + answer = self.resource_manager.get('/api/audio/noise_control/enabled') + return self._result_to_bool(answer.audio.noise_control['enabled']) diff --git a/parrot_zik_tray b/parrot_zik_tray new file mode 100755 index 0000000..f6cc4a0 --- /dev/null +++ b/parrot_zik_tray @@ -0,0 +1,475 @@ +#!/usr/bin/env python +import functools +from threading import Lock + +import gtk + +import bluetooth_paired_devices +from parrot_zik import BatteryStates +from parrot_zik import ParrotZikVersion1 +from parrot_zik import ParrotZikVersion2 +from parrot_zik import NoiseControlTypes +from bluetooth_paired_devices import connect +from parrot_zik import Rooms +from indicator import MenuItem +from indicator import Menu +from indicator import SysIndicator +import resource_manager + +REFRESH_FREQUENCY = 30000 +RECONNECT_FREQUENCY = 5000 + + +class repeat(object): + def __init__(self, f): + self.f = f + self.id = None + self.lock = Lock() + + def __call__(self, cls): + self.f(cls) + + def start(self, cls, frequency): + self.lock.acquire() + if not self.id: + def run(): + self.f(cls) + return True + + self.id = gtk.timeout_add(frequency, run) + self.lock.release() + + def stop(self): + self.lock.acquire() + if self.id: + gtk.timeout_remove(self.id) + self.id = None + self.lock.release() + + +class ParrotZikIndicator(SysIndicator): + def __init__(self): + + self.menu = Menu() + + self.info_item = MenuItem("Parrot Zik Not connected", + None, sensitive=False) + self.menu.append(self.info_item) + + self.version_1_interface = ParrotZikVersion1Interface(self) + self.version_2_interface = ParrotZikVersion2Interface(self) + self.quit = MenuItem("Quit", gtk.main_quit, checkitem=True) + self.menu.append(self.quit) + + SysIndicator.__init__(self, icon="zik-audio-headset", menu=self.menu) + + self.active_interface = None + + @repeat + def reconnect(self): + if self.active_interface: + self.reconnect.stop() + else: + self.info("Trying to connect") + try: + manager = connect() + except bluetooth_paired_devices.BluetoothIsNotOn: + self.info("Bluetooth is turned off") + except bluetooth_paired_devices.DeviceNotConnected: + self.info("Parrot Zik Not connected") + except bluetooth_paired_devices.ConnectionFailure: + self.info("Failed to connect") + else: + if manager.api_version.startswith('1'): + interface = self.version_1_interface + else: + interface = self.version_2_interface + try: + interface.activate(manager) + except resource_manager.DeviceDisconnected: + interface.deactivate() + else: + self.autorefresh(self) + self.autorefresh.start(self, REFRESH_FREQUENCY) + self.reconnect.stop() + + def info(self, message): + self.info_item.set_label(message) + print(message) + + @repeat + def autorefresh(self): + if self.active_interface: + self.active_interface.refresh() + else: + self.reconnect.start(self, RECONNECT_FREQUENCY) + self.autorefresh.stop() + + def main(self): + self.reconnect.start(self, RECONNECT_FREQUENCY) + SysIndicator.main(self) + +class ParrotZikBaseInterface(object): + def __init__(self, indicator): + self.indicator = indicator + self.parrot = None + self.battery_level = MenuItem("Battery Level:", None, sensitive=False, + visible=False) + self.battery_state = MenuItem("Battery State:", None, sensitive=False, + visible=False) + self.firmware_version = MenuItem("Firmware Version:", None, + sensitive=False, visible=False) + self.auto_connection = MenuItem("Auto Connection", self.toggle_auto_connection, + checkitem=True, visible=False) + self.indicator.menu.append(self.battery_level) + self.indicator.menu.append(self.battery_state) + self.indicator.menu.append(self.firmware_version) + self.indicator.menu.append(self.auto_connection) + + def activate(self, manager): + self.parrot = self.parrot_class(manager) + self.read_battery() + self.indicator.info("Connected to: " + self.parrot.friendly_name) + self.firmware_version.set_label( + "Firmware version: " + self.parrot.version) + self.auto_connection.set_active(self.parrot.auto_connect) + self.battery_level.show() + self.battery_state.show() + self.firmware_version.show() + self.auto_connection.show() + 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() + self.battery_state.hide() + self.firmware_version.hide() + self.auto_connection.hide() + self.indicator.menu.reposition() + self.indicator.active_interface = None + self.indicator.setIcon("zik-audio-headset") + self.indicator.info('Lost Connection') + self.indicator.reconnect.start(self.indicator, RECONNECT_FREQUENCY) + + def toggle_auto_connection(self, widget): + try: + self.parrot.auto_connect = self.auto_connection.get_active() + self.auto_connection.set_active(self.parrot.auto_connect) + except resource_manager.DeviceDisconnected: + self.deactivate() + + def refresh(self): + self.read_battery() + + def read_battery(self): + try: + self.parrot.refresh_battery() + battery_level = self.parrot.battery_level + battery_state = self.parrot.battery_state + except resource_manager.DeviceDisconnected: + self.deactivate() + else: + if battery_state == BatteryStates.CHARGING: + self.indicator.setIcon("zik-battery-charging") + elif battery_level > 80: + self.indicator.setIcon("zik-battery-100") + elif battery_level > 60: + self.indicator.setIcon("zik-battery-080") + elif battery_level > 40: + self.indicator.setIcon("zik-battery-060") + elif battery_level > 20: + self.indicator.setIcon("zik-battery-040") + else: + self.indicator.setIcon("zik-battery-low") + + self.battery_state.set_label( + "State: " + BatteryStates.representation[battery_state]) + self.battery_level.set_label( + "Battery Level: " + str(battery_level)) + + +class ParrotZikVersion1Interface(ParrotZikBaseInterface): + parrot_class = ParrotZikVersion1 + + def __init__(self, indicator): + super(ParrotZikVersion1Interface, self).__init__(indicator) + self.noise_cancelation = MenuItem( + "Noise Cancellation", self.toggle_noise_cancelation, + checkitem=True, visible=False) + self.lou_reed_mode = MenuItem("Lou Reed Mode", self.toggle_lou_reed_mode, + checkitem=True, visible=False) + self.concert_hall_mode = MenuItem( + "Concert Hall Mode", self.toggle_parrot_concert_hall, + checkitem=True, visible=False) + self.indicator.menu.append(self.noise_cancelation) + self.indicator.menu.append(self.lou_reed_mode) + self.indicator.menu.append(self.concert_hall_mode) + + def activate(self, manager): + self.noise_cancelation.show() + self.lou_reed_mode.show() + self.concert_hall_mode.show() + super(ParrotZikVersion1Interface, self).activate(manager) + self.noise_cancelation.set_active(self.parrot.cancel_noise) + self.lou_reed_mode.set_active(self.parrot.lou_reed_mode) + self.concert_hall_mode.set_active(self.parrot.concert_hall) + + def deactivate(self): + self.noise_cancelation.hide() + self.lou_reed_mode.hide() + self.concert_hall_mode.hide() + super(ParrotZikVersion1Interface, self).deactivate() + + def toggle_noise_cancelation(self, widget): + try: + self.parrot.cancel_noise = self.noise_cancelation.get_active() + self.noise_cancelation.set_active(self.parrot.cancel_noise) + except resource_manager.DeviceDisconnected: + self.deactivate() + + def toggle_lou_reed_mode(self, widget): + try: + self.parrot.lou_reed_mode = self.lou_reed_mode.get_active() + self.lou_reed_mode.set_active(self.parrot.lou_reed_mode) + self.concert_hall_mode.set_active(self.parrot.concert_hall) + self.concert_hall_mode.set_sensitive( + not self.lou_reed_mode.get_active()) + except resource_manager.DeviceDisconnected: + self.deactivate() + + def toggle_parrot_concert_hall(self, widget): + try: + self.parrot.concert_hall = self.concert_hall_mode.get_active() + self.concert_hall_mode.set_active(self.parrot.concert_hall) + except resource_manager.DeviceDisconnected: + self.deactivate() + + +class ParrotZikVersion2Interface(ParrotZikBaseInterface): + parrot_class = ParrotZikVersion2 + + def __init__(self, indicator): + self.room_dirty = False + self.angle_dirty = False + self.noise_cancelation_dirty = False + super(ParrotZikVersion2Interface, self).__init__(indicator) + self.noise_cancelation = MenuItem("Noise Control", None, visible=False) + self.noise_cancelation_submenu = Menu() + self.noise_cancelation.set_submenu(self.noise_cancelation_submenu) + + self.noise_control_cancelation_max = MenuItem( + "Max Calcelation", functools.partial( + self.toggle_noise_cancelation, + NoiseControlTypes.NOISE_CONTROL_MAX), checkitem=True) + self.noise_control_cancelation_on = MenuItem( + "Normal Cancelation", functools.partial( + self.toggle_noise_cancelation, + NoiseControlTypes.NOISE_CONTROL_ON), checkitem=True) + self.noise_control_off = MenuItem( + "Off", functools.partial( + self.toggle_noise_cancelation, + NoiseControlTypes.NOISE_CONTROL_OFF), checkitem=True) + self.noise_control_street_mode = MenuItem( + "Street Mode", functools.partial( + self.toggle_noise_cancelation, + NoiseControlTypes.STREET_MODE), checkitem=True) + self.noise_control_street_mode_max = MenuItem( + "Street Mode Max", functools.partial( + self.toggle_noise_cancelation, + NoiseControlTypes.STREET_MODE_MAX), checkitem=True) + self.noise_cancelation_submenu.append(self.noise_control_cancelation_max) + self.noise_cancelation_submenu.append(self.noise_control_cancelation_on) + self.noise_cancelation_submenu.append(self.noise_control_off) + self.noise_cancelation_submenu.append(self.noise_control_street_mode) + self.noise_cancelation_submenu.append(self.noise_control_street_mode_max) + + self.room_sound_effect = MenuItem( + "Room Sound Effect", None, visible=False) + self.room_sound_effect_submenu = Menu() + self.room_sound_effect.set_submenu(self.room_sound_effect_submenu) + + self.room_sound_effect_enabled = MenuItem( + "Enabled", self.toggle_room_sound_effect, checkitem=True) + self.rooms = MenuItem("Rooms", None, checkitem=False) + self.angle = MenuItem("Angle", None, checkitem=False) + self.room_sound_effect_submenu.append(self.room_sound_effect_enabled) + self.room_sound_effect_submenu.append(self.rooms) + self.room_sound_effect_submenu.append(self.angle) + + self.rooms_submenu = Menu() + self.rooms.set_submenu(self.rooms_submenu) + + self.concert_hall_mode = MenuItem( + "Concert Hall", functools.partial(self.toggle_room, Rooms.CONCERT_HALL), checkitem=True) + self.jazz_mode = MenuItem( + "Jazz Club", functools.partial(self.toggle_room, Rooms.JAZZ_CLUB), checkitem=True) + self.living_mode = MenuItem( + "Living Room", functools.partial(self.toggle_room, Rooms.LIVING_ROOM), checkitem=True) + self.silent_mode = MenuItem( + "Silent Room", functools.partial(self.toggle_room, Rooms.SILENT_ROOM), checkitem=True) + self.rooms_submenu.append(self.concert_hall_mode) + self.rooms_submenu.append(self.jazz_mode) + self.rooms_submenu.append(self.living_mode) + self.rooms_submenu.append(self.silent_mode) + + self.angle_submenu = Menu() + self.angle.set_submenu(self.angle_submenu) + self.angle_30 = MenuItem( + "30", functools.partial(self.toggle_angle, 30), checkitem=True) + self.angle_60 = MenuItem( + "60", functools.partial(self.toggle_angle, 60), checkitem=True) + self.angle_90 = MenuItem( + "90", functools.partial(self.toggle_angle, 90), checkitem=True) + self.angle_120 = MenuItem( + "120", functools.partial(self.toggle_angle, 120), checkitem=True) + self.angle_150 = MenuItem( + "150", functools.partial(self.toggle_angle, 150), checkitem=True) + self.angle_180 = MenuItem( + "180", functools.partial(self.toggle_angle, 180), checkitem=True) + self.angle_submenu.append(self.angle_30) + self.angle_submenu.append(self.angle_60) + self.angle_submenu.append(self.angle_90) + self.angle_submenu.append(self.angle_120) + self.angle_submenu.append(self.angle_150) + self.angle_submenu.append(self.angle_180) + + self.flight_mode = MenuItem("Flight Mode", self.toggle_flight_mode, + checkitem=True, visible=False) + self.indicator.menu.append(self.room_sound_effect) + self.indicator.menu.append(self.noise_cancelation) + self.indicator.menu.append(self.flight_mode) + + def activate(self, manager): + self.noise_cancelation.show() + self.flight_mode.show() + self.room_sound_effect.show() + super(ParrotZikVersion2Interface, self).activate(manager) + self._read_noise_cancelation() + self.flight_mode.set_active(self.parrot.flight_mode) + self._read_sound_effect_room() + self._read_sound_effect_angle() + sound_effect = self.parrot.sound_effect + + self.room_sound_effect_enabled.set_active(sound_effect) + self.concert_hall_mode.set_sensitive(sound_effect) + self.jazz_mode.set_sensitive(sound_effect) + self.living_mode.set_sensitive(sound_effect) + self.silent_mode.set_sensitive(sound_effect) + + self.angle_30.set_sensitive(sound_effect) + self.angle_60.set_sensitive(sound_effect) + self.angle_90.set_sensitive(sound_effect) + self.angle_120.set_sensitive(sound_effect) + self.angle_150.set_sensitive(sound_effect) + self.angle_180.set_sensitive(sound_effect) + + def deactivate(self): + self.noise_cancelation.hide() + self.concert_hall_mode.hide() + self.flight_mode.hide() + self.room_sound_effect.hide() + super(ParrotZikVersion2Interface, self).deactivate() + + def toggle_flight_mode(self, widget): + try: + self.parrot.flight_mode = self.flight_mode.get_active() + self.flight_mode.set_active(self.parrot.flight_mode) + except resource_manager.DeviceDisconnected: + self.deactivate() + + def toggle_room(self, room, widget): + try: + if not self.room_dirty: + self.parrot.room = room + self.room_dirty = True + self._read_sound_effect_room() + self.room_dirty = False + except resource_manager.DeviceDisconnected: + self.deactivate() + + def _read_sound_effect_room(self): + active_room = self.parrot.room + room_to_menuitem_map = ( + (Rooms.CONCERT_HALL, self.concert_hall_mode), + (Rooms.JAZZ_CLUB, self.jazz_mode), + (Rooms.LIVING_ROOM, self.living_mode), + (Rooms.SILENT_ROOM, self.silent_mode), + ) + for room, menu_item in room_to_menuitem_map: + menu_item.set_active(room == active_room) + + def toggle_room_sound_effect(self, widget): + try: + self.parrot.sound_effect = self.room_sound_effect_enabled.get_active() + sound_effect = self.parrot.sound_effect + self.room_sound_effect_enabled.set_active(sound_effect) + self.concert_hall_mode.set_sensitive(sound_effect) + self.jazz_mode.set_sensitive(sound_effect) + self.living_mode.set_sensitive(sound_effect) + self.silent_mode.set_sensitive(sound_effect) + self.angle_30.set_sensitive(sound_effect) + self.angle_60.set_sensitive(sound_effect) + self.angle_90.set_sensitive(sound_effect) + self.angle_120.set_sensitive(sound_effect) + self.angle_150.set_sensitive(sound_effect) + self.angle_180.set_sensitive(sound_effect) + except resource_manager.DeviceDisconnected: + self.deactivate() + + def toggle_angle(self, angle, widget): + try: + if not self.angle_dirty: + self.parrot.angle = angle + self.angle_dirty = True + self._read_sound_effect_angle() + self.angle_dirty = False + except resource_manager.DeviceDisconnected: + self.deactivate() + + def _read_sound_effect_angle(self): + active_angle = self.parrot.angle + angle_to_menuitem_map = ( + (30, self.angle_30), + (60, self.angle_60), + (90, self.angle_90), + (120, self.angle_120), + (150, self.angle_150), + (180, self.angle_180), + ) + for angle, menu_item in angle_to_menuitem_map: + menu_item.set_active(angle == active_angle) + + def toggle_noise_cancelation(self, noise_calcelation, widget): + try: + if not self.noise_cancelation_dirty: + self.parrot.noise_control = noise_calcelation + self.noise_cancelation_dirty = True + self._read_noise_cancelation() + self.noise_cancelation_dirty = False + except resource_manager.DeviceDisconnected: + self.deactivate() + + def _read_noise_cancelation(self): + active_noise_control = self.parrot.noise_control + noise_control_to_menuitem_map = ( + (NoiseControlTypes.NOISE_CONTROL_MAX, self.noise_control_cancelation_max), + (NoiseControlTypes.NOISE_CONTROL_ON, self.noise_control_cancelation_on), + (NoiseControlTypes.NOISE_CONTROL_OFF, self.noise_control_off), + (NoiseControlTypes.STREET_MODE, self.noise_control_street_mode), + (NoiseControlTypes.STREET_MODE_MAX, self.noise_control_street_mode_max), + ) + for noise_control, menu_item in noise_control_to_menuitem_map: + menu_item.set_active(active_noise_control == noise_control) + + +if __name__ == "__main__": + try: + indicator = ParrotZikIndicator() + indicator.main() + except KeyboardInterrupt: + pass diff --git a/setup.py b/setup.py index 9bb3ae7..7156620 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( windows=[ { - 'script': 'ParrotZikTray', + 'script': 'parrot_zik_tray', 'icon_resources': [(1, "./share/icons/zik/Headphone.ico")], } ], @@ -43,7 +43,7 @@ setup( 'BeautifulSoup', 'bluetooth' ], - py_modules=['ParrotZik', 'message', 'resource_manager'], + py_modules=['parrot_zik', 'message', 'resource_manager'], - scripts=["ParrotZikTray"] + scripts=["parrot_zik_tray"] ) diff --git a/status_app_mac.py b/status_app_mac.py new file mode 100644 index 0000000..dccffe4 --- /dev/null +++ b/status_app_mac.py @@ -0,0 +1,25 @@ +from Foundation import * +from AppKit import * + +class StatusApp(NSApplication): + + def initMenu(self, menu): + statusbar = NSStatusBar.systemStatusBar() + self.statusitem = statusbar.statusItemWithLength_( + NSVariableStatusItemLength) + + self.mymenu = menu + #add menu to statusitem + self.statusitem.setMenu_(menu.menubarMenu) + self.statusitem.setToolTip_('Parrot Zik Indicator') + + def setIcon(self,icon,icon_directory): + self.icon = NSImage.alloc().initByReferencingFile_( + icon_directory + icon + '.png') + self.icon.setScalesWhenResized_(True) + self.icon.setSize_((20, 20)) + self.statusitem.setImage_(self.icon) + + def clicked_(self, notification): + self.mymenu.actions[notification._.title]() + NSLog('clicked!') -- cgit v1.2.1