#!/usr/bin/env python import functools import gtk import BluetoothPairedDevices from ParrotZik import BatteryStates from ParrotZik import NoiseControlTypes from ParrotZik 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: 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) else: self.version_2_interface.activate(parrot) self.autorefresh(self) self.autorefresh.start(self, REFRESH_FREQUENCY) self.reconnect.stop() else: self.info_item.set_label("Failed to connect") else: self.info_item.set_label("Parrot Zik Not connected") @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.api.sock else: return False def activate(self, parrot): self.parrot = parrot self.battery_level.show() self.battery_state.show() self.firmware_version.show() self.auto_connection.show() self.indicator.info_item.set_label("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.indicator.active_interface = self self.indicator.menu.reposition() 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): if self.connected: 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): 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, parrot): self.noise_cancelation.show() self.lou_reed_mode.show() self.concert_hall_mode.show() super(ParrotZikVersion1Interface, self).activate(parrot) self.noise_cancelation.set_active(self.parrot.noise_cancel) 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): 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, parrot): self.noise_cancelation.show() self.flight_mode.show() self.room_sound_effect.show() super(ParrotZikVersion2Interface, self).activate(parrot) 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