#!/usr/bin/env python import functools 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 REFRESH_FREQUENCY = 30000 RECONNECT_FREQUENCY = 5000 class repeat(object): def __init__(self, f): self.f = f self.id = None def __call__(self, cls): self.f(cls) def start(self, cls, frequency): if not self.id: def run(): self.f(cls) return True self.id = gtk.timeout_add(frequency, run) def stop(self): if self.id: gtk.timeout_remove(self.id) self.id = None 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: if not self.active_interface.connected: self.info_item.set_label("Lost connection") self.active_interface.deactivate() else: self.reconnect.stop() else: self.info("Trying to connect") try: resource_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 resource_manager.api_version.startswith('1'): self.version_1_interface.activate(resource_manager) else: self.version_2_interface.activate(resource_manager) 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) @property def connected(self): if self.parrot: return self.parrot.resource_manager.sock else: return False def activate(self, resource_manager): self.parrot = self.parrot_class(resource_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.setIcon("zik-audio-headset") self.indicator.info_item.set_label("Parrot Zik Not connected..") self.indicator.menu.reposition() self.indicator.active_interface = None def toggle_auto_connection(self, widget): if self.connected: self.parrot.auto_connect = self.auto_connection.get_active() self.auto_connection.set_active(self.parrot.auto_connect) else: self.deactivate() def refresh(self): self.read_battery() 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: 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)) else: self.deactivate() 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, resource_manager): self.noise_cancelation.show() self.lou_reed_mode.show() self.concert_hall_mode.show() 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) 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): if self.connected: self.parrot.cancel_noise = self.noise_cancelation.get_active() self.noise_cancelation.set_active(self.parrot.cancel_noise) else: self.deactivate() def toggle_lou_reed_mode(self, widget): if self.connected: 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()) else: self.deactivate() def toggle_parrot_concert_hall(self, widget): if self.connected: self.parrot.concert_hall = self.concert_hall_mode.get_active() self.concert_hall_mode.set_active(self.parrot.concert_hall) else: 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, resource_manager): self.noise_cancelation.show() self.flight_mode.show() self.room_sound_effect.show() super(ParrotZikVersion2Interface, self).activate(resource_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): if self.connected: self.parrot.flight_mode = self.flight_mode.get_active() self.flight_mode.set_active(self.parrot.flight_mode) else: self.deactivate() def toggledummy(self, widget): print(widget.get_name()) def toggle_room(self, room, widget): if self.connected: if not self.room_dirty: self.parrot.room = room self.room_dirty = True self.read_sound_effect_room() self.room_dirty = False else: 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): if self.connected: 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) else: self.deactivate() def toggle_angle(self, angle, widget): if self.connected: if not self.angle_dirty: self.parrot.angle = angle self.angle_dirty = True self.read_sound_effect_angle() self.angle_dirty = False else: 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): if self.connected: 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 else: 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