EventGhost/EventGhost

View on GitHub
plugins/XBMCRepeat/__init__.py

Summary

Maintainability
F
1 mo
Test Coverage
# Copyright (C) 2008 Chris Longo <cal@chrislongo.net> and Tobias Arrskog (topfs2)
#
# This file is a plugin for EventGhost.
#
# EventGhost is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# EventGhost is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EventGhost; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

from xbmcclient import *
import urllib2
import json
import ast
import xml.dom.minidom
from xml.dom.minidom import Node
import socket
import base64
from urlparse import urlparse
import os
import pickle

from threading import Event, Thread

# expose some information about the plugin through an eg.PluginInfo subclass

eg.RegisterPlugin(
    name = "XBMC2",
    author = "Joni Boren",
    version = "0.6.35",
    kind = "program",
    guid = "{8C8B850C-773F-4583-AAD9-A568262B7933}",
    canMultiLoad = True,
    createMacrosOnAdd = True,
    url = "http://www.eventghost.net/forum/viewtopic.php?f=10&t=1562",
    description = "Adds actions buttons to control <a href='http://www.xbmc.org/'>XBMC</a>.",
    icon = (
        "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA"
        "BGdBTUEAALGeYUxB9wAAACBjSFJNAAB6fAAAfosAAPoBAACHPAAAbUoAAPFDAAA2IAAAHlNX4WK7"
        "AAACYElEQVR42tTTPW8ScQDH8d/9n44HESgFKQjFWku1rUYdTEAT0sGHSU10cHZz0piYjqYxbia6"
        "+hB3Y0w3kzq0scaojdZqYiMp1dQWAlLg4A7ugbvzRXTy8wK+21dyXRe7QbBLuw6wG3ceJnJnLuTL"
        "279DrutySBIhEnUopSbjtMsYVzkXLSFEU5Y9dY/XW5Nlr207DpRWE+zzp6VHxWLpstpWKaEA5RT9"
        "XgCcC/jDDihjOpWI6vF4WkLweigULgdD4R/p4ZH30X1Dr6XhbK4i/OH43qSKVikJLhhGz26AEo61"
        "+Qz0roWR8RDWixtIJKP4/mUVA5EgkvvjOHEy/1FKj+XnwpHMxdipIhJH29C2o0hMVmH1KJQyxWTw"
        "FuKhKYCbaDUVOI4LwzKxOD8PAvkrMazOW1uSUH43ilCqgUYphvJyBitzKUyfLiCVBe7PPkVzp4l7"
        "dx9g+lwB5T9bePPqJTIjB4v0uqmVi4cHbx67UkFteRjRAx30mgEcym9iZz2NpRcyfAM6Om0Nruui"
        "sr2F8SNZuIQjEhl6Lj0LAY8Hcwtq6nwhStuQJB8sWOh3fTClBgIDOhj1wDAtcEFRq/5FW+shPRRF"
        "diyTYJNe4Kr1bfaJHiv0qAtBKTgX4D6CAJXAbQIhaYhyG16iIxvpwEfW0BITM75YrsJm3Ah6SnfB"
        "kCtzWmLikmabYLYAIRxO34Zp6nAs9VdX6xSVRn2lb7QWe2b3w9RxplwLy2AL8AOMIa5s3vb6gzUm"
        "+5mh1XXL0Lq2pVRVQ2z66J6fpLdaMqu6KjwUXo8XnFH0+w6k/3+mfwMAzwT87LI0qNEAAAAASUVO"
        "RK5CYII="
    ),
)

"""
'http://www.xbmc.org/'
'http://wiki.xbmc.org/index.php?title=Web_Server_HTTP_API'
http://xbmc.org/wiki/?title=Window_IDs
http://xbmc.org/wiki/?title=Action_IDs#General_actions_available_throughout_most_of_XBMC
'http://wiki.xbmc.org/?title=Action_IDs'
'https://raw.githubusercontent.com/xbmc/xbmc/master/xbmc/input/ButtonTranslator.cpp'
"""
# from threading import Event, Thread

# Windows availible in XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Window_IDs http://kodi.wiki/view/Window_IDs
"""
"""

WINDOWS = (
(eg.ActionGroup, "Windows", "Windows", None, (
    ("MyMovies", "Show Movies Screen", "Show Movies screen.", "Activatewindow(MyVideoLibrary,movietitles,return)"),
    ("MyTVShows", "Show TV Shows Screen", "Show TV Shows screen.", "Activatewindow(MyVideoLibrary,tvshowtitles,return)"),
    ("ShutdownMenu", "Show Shutdown Menu", "Show the shutdown Menu.", "Activatewindow(ShutdownMenu)"),
    ("Home", "Home", "WINDOW_HOME", "Activatewindow(Home)"),
    ("Programs", "Programs", "WINDOW_PROGRAMS", "Activatewindow(Programs)"),
    ("Pictures", "Pictures", "WINDOW_PICTURES", "Activatewindow(Pictures)"),
    ("Files", "Files", "WINDOW_FILES\nbackward compat", "Activatewindow(Files)"),
    ("Settings", "Settings", "WINDOW_SETTINGS_MENU", "Activatewindow(Settings)"),
    ("Music", "Music", "WINDOW_MUSIC", "Activatewindow(Music)"),
    ("Musicfiles", "Musicfiles", "WINDOW_MUSIC_FILES", "Activatewindow(Musicfiles)"),
    ("Musiclibrary", "Musiclibrary", "WINDOW_MUSIC_NAV", "Activatewindow(Musiclibrary)"),
    ("Musicplaylist", "Musicplaylist", "WINDOW_MUSIC_PLAYLIST", "Activatewindow(Musicplaylist)"),
    ("Musicplaylisteditor", "Musicplaylisteditor", "WINDOW_MUSIC_PLAYLIST_EDITOR", "Activatewindow(Musicplaylisteditor)"),
    ("Musicinformation", "Musicinformation", "WINDOW_DIALOG_MUSIC_INFO", "Activatewindow(Musicinformation)"),
    ("Video", "Video", "WINDOW_VIDEOS", "Activatewindow(Video)"),
    ("Videofiles", "Videofiles", "WINDOW_VIDEO_FILES", "Activatewindow(Videofiles)"),
    ("Videolibrary", "Videolibrary", "WINDOW_VIDEO_NAV", "Activatewindow(Videolibrary)"),
    ("Videoplaylist", "Videoplaylist", "WINDOW_VIDEO_PLAYLIST", "Activatewindow(Videoplaylist)"),
    ("Systeminfo", "Systeminfo", "WINDOW_SYSTEM_INFORMATION", "Activatewindow(Systeminfo)"),
    ("Guicalibration", "Guicalibration", "WINDOW_SCREEN_CALIBRATION\nbackward compat", "Activatewindow(Guicalibration)"),
    ("Screencalibration", "Screencalibration", "WINDOW_SCREEN_CALIBRATION", "Activatewindow(Screencalibration)"),
    ("Picturessettings", "Picturessettings", "WINDOW_SETTINGS_MYPICTURES", "Activatewindow(Picturessettings)"),
    ("Programssettings", "Programssettings", "WINDOW_SETTINGS_MYPROGRAMS", "Activatewindow(Programssettings)"),
    ("Weathersettings", "Weathersettings", "WINDOW_SETTINGS_MYWEATHER", "Activatewindow(Weathersettings)"),
    ("Musicsettings", "Musicsettings", "WINDOW_SETTINGS_MYMUSIC", "Activatewindow(Musicsettings)"),
    ("Systemsettings", "Systemsettings", "WINDOW_SETTINGS_SYSTEM", "Activatewindow(Systemsettings)"),
    ("Videossettings", "Videossettings", "WINDOW_SETTINGS_MYVIDEOS", "Activatewindow(Videossettings)"),
    ("Networksettings", "Networksettings", "WINDOW_SETTINGS_NETWORK", "Activatewindow(Networksettings)"),
    ("Appearancesettings", "Appearancesettings", "WINDOW_SETTINGS_APPEARANCE", "Activatewindow(Appearancesettings)"),
    ("Scripts", "Scripts", "WINDOW_PROGRAMS\nbackward compat", "Activatewindow(Scripts)"),
    ("Gamesaves", "Gamesaves", "Gamesaves", "Activatewindow(Gamesaves)"),
    ("Profiles", "Profiles", "WINDOW_SETTINGS_PROFILES", "Activatewindow(Profiles)"),
    ("Virtualkeyboard", "Virtualkeyboard", "WINDOW_DIALOG_KEYBOARD", "Activatewindow(Virtualkeyboard)"),
    ("Volumebar", "Volumebar", "WINDOW_DIALOG_VOLUME_BAR", "Activatewindow(Volumebar)"),
    ("Favourites", "Favourites", "WINDOW_DIALOG_FAVOURITES", "Activatewindow(Favourites)"),
    ("Musicosd", "Musicosd", "WINDOW_DIALOG_MUSIC_OSD", "Activatewindow(Musicosd)"),
    ("Visualisationsettings", "Visualisationsettings", "WINDOW_DIALOG_ADDON_SETTINGS\nbackward compat", "Activatewindow(Visualisationsettings)"),
    ("Visualisationpresetlist", "Visualisationpresetlist", "WINDOW_DIALOG_VIS_PRESET_LIST", "Activatewindow(Visualisationpresetlist)"),
    ("Osdvideosettings", "Osdvideosettings", "WINDOW_DIALOG_VIDEO_OSD_SETTINGS", "Activatewindow(Osdvideosettings)"),
    ("Osdaudiosettings", "Osdaudiosettings", "WINDOW_DIALOG_AUDIO_OSD_SETTINGS", "Activatewindow(Osdaudiosettings)"),
    ("Videobookmarks", "Videobookmarks", "WINDOW_DIALOG_VIDEO_BOOKMARKS", "Activatewindow(Videobookmarks)"),
    ("Profilesettings", "Profilesettings", "WINDOW_DIALOG_PROFILE_SETTINGS", "Activatewindow(Profilesettings)"),
    ("Locksettings", "Locksettings", "WINDOW_DIALOG_LOCK_SETTINGS", "Activatewindow(Locksettings)"),
    ("Contentsettings", "Contentsettings", "WINDOW_DIALOG_CONTENT_SETTINGS", "Activatewindow(Contentsettings)"),
    ("Networksetup", "Networksetup", "WINDOW_DIALOG_NETWORK_SETUP", "Activatewindow(Networksetup)"),
    ("Smartplaylisteditor", "Smartplaylisteditor", "WINDOW_DIALOG_SMART_PLAYLIST_EDITOR", "Activatewindow(Smartplaylisteditor)"),
    ("Smartplaylistrule", "Smartplaylistrule", "WINDOW_DIALOG_SMART_PLAYLIST_RULE", "Activatewindow(Smartplaylistrule)"),
    ("Movieinformation", "Movieinformation", "WINDOW_DIALOG_VIDEO_INFO", "Activatewindow(Movieinformation)"),
    ("Scriptsdebuginfo", "Scriptsdebuginfo", "Scriptsdebuginfo", "Activatewindow(Scriptsdebuginfo)"),
    ("Fullscreenvideo", "Fullscreenvideo", "WINDOW_FULLSCREEN_VIDEO", "Activatewindow(Fullscreenvideo)"),
    ("Visualisation", "Visualisation", "WINDOW_VISUALISATION", "Activatewindow(Visualisation)"),
    ("Slideshow", "Slideshow", "WINDOW_SLIDESHOW", "Activatewindow(Slideshow)"),
    ("Filestackingdialog", "Filestackingdialog", "WINDOW_DIALOG_FILESTACKING", "Activatewindow(Filestackingdialog)"),
    ("Weather", "Weather", "WINDOW_WEATHER", "Activatewindow(Weather)"),
    ("Screensaver", "Screensaver", "WINDOW_SCREENSAVER", "Activatewindow(Screensaver)"),
    ("Videoosd", "Videoosd", "WINDOW_DIALOG_VIDEO_OSD", "Activatewindow(Videoosd)"),
    ("Videomenu", "Videomenu", "WINDOW_VIDEO_MENU", "Activatewindow(Videomenu)"),
    ("Filebrowser", "Filebrowser", "WINDOW_DIALOG_FILE_BROWSER", "Activatewindow(Filebrowser)"),
    ("Startup", "Startup", "WINDOW_STARTUP_ANIM", "Activatewindow(Startup)"),
    ("Startwindow", "Startwindow", "WINDOW_START", "Activatewindow(Startwindow)"),
    ("Loginscreen", "Loginscreen", "WINDOW_LOGIN_SCREEN", "Activatewindow(Loginscreen)"),
    ("Musicoverlay", "Musicoverlay", "WINDOW_DIALOG_MUSIC_OVERLAY", "Activatewindow(Musicoverlay)"),
    ("Videooverlay", "Videooverlay", "WINDOW_DIALOG_VIDEO_OVERLAY", "Activatewindow(Videooverlay)"),
    ("Pictureinfo", "Pictureinfo", "WINDOW_DIALOG_PICTURE_INFO", "Activatewindow(Pictureinfo)"),
    ("Pluginsettings", "Pluginsettings", "Pluginsettings", "Activatewindow(Pluginsettings)"),
    ("Fullscreeninfo", "Fullscreeninfo", "WINDOW_DIALOG_FULLSCREEN_INFO", "Activatewindow(Fullscreeninfo)"),
    ("PlayerControls", "Player Controls", "WINDOW_DIALOG_PLAYER_CONTROLS", "Activatewindow(Playercontrols)"),
)),
)
# actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#General_actions_available_throughout_most_of_XBMC http://kodi.wiki/view/Action_IDs#General_actions_available_throughout_most_of_XBMC

GENERAL_ACTIONS = (
(eg.ActionGroup, "General", "General", None, (
    ("Left", "Left", "Move left off a control.", "left"),
    ("Right", "Right", "Move right off a control.", "right"),
    ("Up", "Up", "Move up off a control.", "up"),
    ("Down", "Down", "Move down off a control.", "down"),
    ("PageUp", "PageUp", "Scroll up on page in a list, thumb, or text view.", "pageup"),
    ("PageDown", "PageDown", "Scroll down on page in a list, thumb, or text view.", "pagedown"),
    ("Select", "Select", "Select a button, or an item from a list of thumb view.", "select"),
    ("Highlight", "Highlight", "Highlight an item in a list or thumb view.", "highlight"),
    ('Parentfolder', 'Parentfolder', 'Go up a folder to the parent folder.', 'parentfolder'),
    ('Back', 'Back', '', 'back'),
    ("ParentDir", "ParentDir", "Go up a folder to the parent folder.\n// backward compatibility", "parentdir"),
    ("PreviousMenu", "PreviousMenu", "Go back to the previous menu screen.", "previousmenu"),
    ("Info", "Info", "Show the information about the currently highlighted item, or currently playing item.", "info"),
    ("Screenshot", "Screenshot", "Take a screenshot of the current screen.", "screenshot"),
    ("PowerOff", "PowerOff", "Shutdown and power off.", "poweroff"),
    ("VolumeUp", "VolumeUp", "Increase the volume of playback.", "volumeup"),
    ("VolumeDown", "VolumeDown", "Decrease the volume of playback.", "volumedown"),
    ("Mute", "Mute", "Mute the volume.", "mute"),
    ("ContextMenu", "ContextMenu", "Pops up a contextual menu", "contextmenu"),
    ("ScrollUp", "ScrollUp", "Variable speed scroll up for analog keys (stick or triggers)", "scrollup"),
    ("ScrollDown", "ScrollDown", "Variable speed scroll down for analog keys (stick or triggers)", "scrolldown"),
    ("Close", "Close", "Used to close a dialog", "close"),
    ("Number0", "Number0", "Used to input the number 0", "number0"),
    ("Number1", "Number1", "Used to input the number 1", "number1"),
    ("Number2", "Number2", "Used to input the number 2", "number2"),
    ("Number3", "Number3", "Used to input the number 3", "number3"),
    ("Number4", "Number4", "Used to input the number 4", "number4"),
    ("Number5", "Number5", "Used to input the number 5", "number5"),
    ("Number6", "Number6", "Used to input the number 6", "number6"),
    ("Number7", "Number7", "Used to input the number 7", "number7"),
    ("Number8", "Number8", "Used to input the number 8", "number8"),
    ("Number9", "Number9", "Used to input the number 9", "number9"),
)),
)

# actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#General_actions_available_while_video_or_music_are_playing http://kodi.wiki/view/Action_IDs#General_actions_available_while_video_or_music_are_playing

MEDIA_PLAYING_ACTIONS = (
(eg.ActionGroup, "MediaPlaying", "Media playing", None, (
    ("Play", "Play", "Play the selected item (or folder of items), or unpause a paused item.", "play"),
    ("Pause", "Pause", "Pause the currently playing item.", "pause"),
    ("Stop", "Stop", "Stop the currently playing item.", "stop"),
    ("FastForward", "FastForward", "Toggle the fastforward speed between normal play, 2x, 4x, 8x, 16x, and 32x.", "fastforward"),
    ("Rewind", "Rewind", "Toggle the rewind speed between normal play, 2x, 4x, 8x, 16x, and 32x.", "rewind"),
    ("SkipNext", "SkipNext", "Skip to the next item in a playlist or scene in a video.", "skipnext"),
    ("SkipPrevious", "SkipPrevious", "Skip to the previous item in a playlist or scene in a video.", "skipprevious"),
    ("FullScreen", "FullScreen", "Toggles fullscreen modes (either visualisation or video playback)", "fullscreen"),
    ("CodecInfo", "CodecInfo", "Show codec information about the currently playing item (during video or visualisation playback)", "codecinfo"),
    ("AnalogSeekForward", "AnalogSeekForward", "Variable speed seeking for analog keys (stick or triggers)", "analogseekforward"),
    ("AnalogSeekBack", "AnalogSeekBack", "Variable speed seeking for analog keys (stick or triggers)", "analogseekback"),
    ("AnalogFastForward", "AnalogFastForward", "Variable speed fast forward for analog keys (stick or triggers)", "analogfastforward"),
    ("AnalogRewind", "AnalogRewind", "Variable speed rewind for analog keys (stick or triggers)", "analogrewind"),
    ("PartyMode", "Party Mode", "Party mode.", "playercontrol(partymode)"),
    ("Random", "Random", "Toggles random playback", "playercontrol(Random,Notify)"),
    ("Repeat", "Repeat", "Cycles through the repeat modes", "playercontrol(Repeat,Notify)"),
    ("UpdateVideoLibrary", "Update Video Library", "Update the video library.", "updatelibrary(video)"),
    ("UpdateMusicLibrary", "Update Music Library", "Update the music library.", "updatelibrary(music)"),
    ("IncreaseRating", "IncreaseRating", "Unused.", "increaserating"),
    ("DecreaseRating", "DecreaseRating", "Unused .", "decreaserating"),
    ("EjectTray", "EjectTray", "Close or open the DVD tray", "EjectTray"),
    ("Record", "Record", "Starts recording.", "record"),
    ("PlayDVD", "PlayDVD", "Plays the inserted CD or DVD media from the DVD-ROM Drive!", "PlayDVD"),
    ("LastFMLove", "LastFM.Love", "Add the current playing last.fm radio track to the last.fm loved tracks", "LastFM.Love"),
    ("LastFMBan", "LastFM.Ban", "Ban the current playing last.fm radio track", "LastFM.Ban"),
)),
)

# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_only_in_Music_and_Videos_windows_only http://kodi.wiki/view/Action_IDs#Actions_available_only_in_Music_and_Videos_windows_only

PLAYLIST_ACTIONS = (
(eg.ActionGroup, "Playlist", "Playlist", None, (
    ("Playlist", "Playlist", "Toggle to playlist view from My Music or My Videos", "playlist"),
    ("Queue", "Queue", "Queue the item to the current playlist", "queue"),
    ("MoveItemUp", "MoveItemUp", "Used to rearrange playlists", "moveitemup"),
    ("MoveItemDown", "MoveItemDown", "Used to rearrange playlists", "moveitemdown"),
)),
)

# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_only_in_Full_Screen_Video http://kodi.wiki/view/Action_IDs#Actions_available_only_in_Full_Screen_Video

FULLSCREEN_VIDEO_ACTIONS = (
(eg.ActionGroup, "FullscreenVideo", "FullScreen Video", None, (
    ("StepForward", "StepForward", "Step forward 1% in the movie.", "stepforward"),
    ("StepBack", "StepBack", "Step back 1% in the movie.", "stepback"),
    ("BigStepForward", "BigStepForward", "Step forward 10% in the movie.", "bigstepforward"),
    ("BigStepBack", "BigStepBack", "Step back 10% in the movie.", "bigstepback"),
    ("SmallStepBack", "SmallStepBack", "Step back 7 seconds in the current video.", "smallstepback"),
    ("OSD", "OSD", "Toggles the OSD while playing an item.", "osd"),
    ("AspectRatio", "AspectRatio", "Toggle through the various aspect ratio modes (Normal is the preferred option).", "aspectratio"),
    ("ShowSubtitles", "ShowSubtitles", "Toggles whether subtitles are shown or not.", "showsubtitles"),
    ("NextSubtitle", "NextSubtitle", "Change to the next subtitle language, if there is more than one.", "nextsubtitle"),
    ('Subtitledelay', 'SubtitleDelay', 'Show subtitle delay slider', 'subtitledelay'),
    ("SubtitleDelayMinus", "SubtitleDelayMinus", "Decrease the delay amount of subtitles (use if subtitles are displaying too late)", "subtitledelayminus"),
    ("SubtitleDelayPlus", "SubtitleDelayPlus", "Increase the delay amount of subtitles (use if subtitles are displaying too early)", "subtitledelayplus"),
    ('Audiodelay', 'AudioDelay', 'Show audio delay slider', 'audiodelay'),
    ("AudioDelayMinus", "AudioDelayMinus", "Decrease the delay amount of audio (use if audio is being heard too early)", "audiodelayminus"),
    ("AudioDelayPlus", "AudioDelayPlus", "Increase the delay amount of audio (use if audio is being heard too late)", "audiodelayplus"),
    ("AudioNextLanguage", "AudioNextLanguage", "Change to the next audio track in a video with multiple audio tracks.", "audionextlanguage"),
    ("mplayerosd", "MplayerOSD", "Show Mplayer's OSD", "mplayerosd"),
    ("ShowTime", "ShowTime", "Used to show the current play time in music + video playback", "showtime"),
    ("ShowVideoMenu", "ShowVideoMenu", "Go to the DVD Video menu when playing a DVD.", "showvideomenu"),
    ('Increasepar', 'IncreasePAR', 'Used in video fullscreen to increase the pixel aspect ratio (stretch).', 'increasepar'),
    ('Decreasepar', 'DecreasePAR', 'Used in video fullscreen to decrease the pixel aspect ratio (stretch).', 'decreasepar'),
)),
)

# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_during_a_picture_slideshow http://kodi.wiki/view/Action_IDs#Actions_available_during_a_picture_slideshow

SLIDESHOW_ACTIONS = (
(eg.ActionGroup, "PictureSlideshow", "Picture slideshow", None, (
    ("NextPicture", "NextPicture", "Move to the next picture in a slideshow.", "nextpicture"),
    ("PreviousPicture", "PreviousPicture", "Move to the previous picture in a slideshow.", "previouspicture"),
    ("ZoomOut", "ZoomOut", "Used in picture, slideshow or video fullscreen to zoom out of the current image/video.", "zoomout"),
    ("ZoomIn", "ZoomIn", "Used in picture, slideshow or video fullscreen to zoom in to the current image/video.", "zoomin"),
    ("ZoomNormal", "ZoomNormal", "Normal (fullscreen) viewing in My Pictures", "zoomnormal"),
    ("ZoomLevel1", "ZoomLevel1", "Zoom to 120% in My Pictures", "zoomlevel1"),
    ("ZoomLevel2", "ZoomLevel2", "Zoom to 150% in My Pictures", "zoomlevel2"),
    ("ZoomLevel3", "ZoomLevel3", "Zoom to 200% in My Pictures", "zoomlevel3"),
    ("ZoomLevel4", "ZoomLevel4", "Zoom to 280% in My Pictures", "zoomlevel4"),
    ("ZoomLevel5", "ZoomLevel5", "Zoom to 400% in My Pictures", "zoomlevel5"),
    ("ZoomLevel6", "ZoomLevel6", "Zoom to 600% in My Pictures", "zoomlevel6"),
    ("ZoomLevel7", "ZoomLevel7", "Zoom to 900% in My Pictures", "zoomlevel7"),
    ("ZoomLevel8", "ZoomLevel8", "Zoom to 1350% in My Pictures", "zoomlevel8"),
    ("ZoomLevel9", "ZoomLevel9", "Zoom to 2000% in My Pictures", "zoomlevel9"),
    ("AnalogMove", "AnalogMove", "Move in the calibration screens, and while zoomed in My Pictures.", "analogmove"),
    ("Rotate", "Rotate", "Rotate a picture in My Pictures", "rotate"),
)),
)
# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_in_screen_calibration http://kodi.wiki/view/Action_IDs#Actions_available_in_screen_calibration

CALIBRATION_ACTIONS = (
(eg.ActionGroup, "ScreenCalibration", "Screen calibration", None, (
    ("NextCalibration", "NextCalibration", "Used in Video + GUI calibration", "nextcalibration"),
    ("ResetCalibration", "ResetCalibration", "Used in Video + GUI calibration", "resetcalibration"),
    ("AnalogMove", "AnalogMove", "Move in the calibration screens, and while zoomed in My Pictures.", "analogmove"),
    ("NextResolution", "NextResolution", "Used in Video calibration", "nextresolution"),
)),
)

# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_in_the_File_Manager http://kodi.wiki/view/Action_IDs#Actions_available_in_the_File_Manager

FILEMANAGER_ACTIONS = (
(eg.ActionGroup, "FileManager", "File Manager", None, (
    ("Delete", "Delete", "Used in My Files to delete a file.", "delete"),
    ("Copy", "Copy", "Used in My Files to copy a file.", "copy"),
    ("Move", "Move", "Used in My Files to move a file.", "move"),
    ("Rename", "Rename", "Used in My Files to rename a file.", "rename"),
)),
)
# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_in_the_on-screen_keyboard http://kodi.wiki/view/Action_IDs#Actions_available_in_the_on-screen_keyboard

ON_SCREEN_KEYBOARD_ACTIONS = (
(eg.ActionGroup, "On-screenKeyboard", "On-screen keyboard", None, (
    ("BackSpace", "BackSpace", "Used in the virtual keyboards to delete one letter.", "backspace"),
    ("Shift", "Shift", "Used in Virtual Keyboard to switch to upper or lower case letters", "shift"),
    ("Symbols", "Symbols", "Used in Virtual Keyboard to switch to or from symbols mode", "symbols"),
    ("CursorLeft", "CursorLeft", "Used in Virtual Keyboard to move the current cursor point to the left", "cursorleft"),
    ("CursorRight", "CursorRight", "Used in Virtual Keyboard to move the current cursor point to the right", "cursorright"),
)),
)

# Actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#Actions_available_during_a_music_visualisation http://kodi.wiki/view/Action_IDs#Actions_available_during_a_music_visualisation

VISUALISATION_ACTIONS = (
(eg.ActionGroup, "MusicVisualisation", "Music visualisation", None, (
    ("OSD", "OSD", "Toggles the OSD while playing an item.", "osd"),
    ("ShowPreset", "ShowPreset", "Shows the current visualisation preset (milkdrop/spectrum)", "showpreset"),
    ("PresetList", "PresetList", "Pops up the visualisation preset list (milkdrop/spectrum)", "presetlist"),
    ("NextPreset", "NextPreset", "Next visualisation preset", "nextpreset"),
    ("PreviousPreset", "PreviousPreset", "Previous visualisation preset", "previouspreset"),
    ("LockPreset", "LockPreset", "Lock the current visualisation preset", "lockpreset"),
    ("RandomPreset", "RandomPreset", "Switch to a new random preset", "randompreset"),
    ("increasevisrating", "IncreaseVisRating", "", "increasevisrating"),
    ("decreasevisrating", "DecreaseVisRating", "", "decreasevisrating"),
)),
)

SHUTDOWN_ACTIONS = (
(eg.ActionGroup, "ShutdownRelated", "Shutdown related", None, (
    ("Quit", "Quit", "Quit XBMC", "Quit"),
    ("RestartApp", "RestartApp", "Restart XBMC", "RestartApp"),
    ("Reset", "Reset Computer", "Reset the computer.", "reset"),
    ("Shutdown", "Shutdown Computer", "Trigger default Shutdown action defined in System Settings, Default Quit on Windows.", "shutdown"),
    ("Powerdown", "Powerdown", "Powerdown system", "Powerdown"),
    ("Suspend", "Suspend", "Suspends the system", "Suspend"),
    ("Hibernate", "Hibernate", "Hibernates the system", "Hibernate"),
    ("Reboot", "Reboot Computer", "Cold reboots the system (power cycle).", "reboot"),
    ("Restart", "Restart Computer", "Cold reboots the system (power cycle).", "restart"),
)),
)

UNCATEGORIZED_ACTIONS = (
(eg.ActionGroup, "UncategorizedActions", "Uncategorized actions", None, (
    ("JumpSMS2", "JumpSMS2", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms2"),
    ("JumpSMS3", "JumpSMS3", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms3"),
    ("JumpSMS4", "JumpSMS4", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms4"),
    ("JumpSMS5", "JumpSMS5", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms5"),
    ("JumpSMS6", "JumpSMS6", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms6"),
    ("JumpSMS7", "JumpSMS7", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms7"),
    ("JumpSMS8", "JumpSMS8", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms8"),
    ("JumpSMS9", "JumpSMS9", "Jump through a list using SMS-style input (eg press 2 twice to jump to the B's.)", "jumpsms9"),
    ("FilterClear", "FilterClear", "", "filterclear"),
    ("FilterSMS2", "FilterSMS2", "Filter a list in music or videos using SMS-style input.", "filtersms2"),
    ("FilterSMS3", "FilterSMS3", "Filter a list in music or videos using SMS-style input.", "filtersms3"),
    ("FilterSMS4", "FilterSMS4", "Filter a list in music or videos using SMS-style input.", "filtersms4"),
    ("FilterSMS5", "FilterSMS5", "Filter a list in music or videos using SMS-style input.", "filtersms5"),
    ("FilterSMS6", "FilterSMS6", "Filter a list in music or videos using SMS-style input.", "filtersms6"),
    ("FilterSMS7", "FilterSMS7", "Filter a list in music or videos using SMS-style input.", "filtersms7"),
    ("FilterSMS8", "FilterSMS8", "Filter a list in music or videos using SMS-style input.", "filtersms8"),
    ("FilterSMS9", "FilterSMS9", "Filter a list in music or videos using SMS-style input.", "filtersms9"),
    ("FirstPage", "FirstPage", "", "firstpage"),
    ("LastPage", "LastPage", "", "lastpage"),

    ("HideSubMenu", "HideSubMenu", "<depreciated>", "hidesubmenu"),

    ("ToggleSource", "ToggleSource", "", "togglesource"),
    ("Remove", "Remove", "", "remove"),

    ("AudioToggleDigital", "AudioToggleDigital", "", "audiotoggledigital"),

    ("OSDLeft", "OSDLeft", "", "osdleft"),
    ("OSDRight", "OSDRight", "", "osdright"),
    ("OSDUp", "OSDUp", "", "osdup"),
    ("OSDDown", "OSDDown", "", "osddown"),
    ("OSDSelect", "OSDSelect", "", "osdselect"),
    ("OSDValuePlus", "OSDValuePlus", "", "osdvalueplus"),
    ("OSDValueMinus", "OSDValueMinus", "", "osdvalueminus"),

    ("ToggleWatched", "ToggleWatched", "Toggles watched/unwatched status for Videos", "togglewatched"),
    ("ScanItem", "ScanItem", "", "scanitem"),

    ("Enter", "Enter", "", "enter"),
    ("IncreaseRating", "IncreaseRating", "Unused", "increaserating"),
    ("DecreaseRating", "DecreaseRating", "Unused", "decreaserating"),
    ("ToggleFullScreen", "ToggleFullScreen", "", "togglefullscreen"),
    ("NextScene", "NextScene", "", "nextscene"),
    ("PreviousScene", "PreviousScene", "", "previousscene"),
    ("NextLetter", "NextLetter", "Move to the next letter in a list or thumb panel.  Note that SHIFT-B on the keyboard will take you to the B's.", "nextletter"),
    ("PrevLetter", "PrevLetter", "Move to the previous letter in a list or thumb panel.  Note that SHIFT-Z on the keyboard will take you to the Z's.", "prevletter"),

    ('Verticalshiftup', 'VerticalShiftUp', '', 'verticalshiftup'),
    ('Verticalshiftdown', 'VerticalShiftDown', '', 'verticalshiftdown'),
    ('Playpause', 'PlayPause', '', 'playpause'),
    ('Reloadkeymaps', 'ReloadKeymaps', '', 'reloadkeymaps'),
    ('Guiprofile', 'GuiProfile', '', 'guiprofile'),
    ('Red', 'Red', '', 'red'),
    ('Green', 'Green', '', 'green'),
    ('Yellow', 'Yellow', '', 'yellow'),
    ('Blue', 'Blue', '', 'blue'),

    ('Subtitleshiftup', 'Subtitleshiftup', '', 'subtitleshiftup'),
    ('Subtitleshiftdown', 'Subtitleshiftdown', '', 'subtitleshiftdown'),
    ('Subtitlealign', 'Subtitlealign', '', 'subtitlealign'),
    ('Help', 'Help', 'This help message', 'Help'),
    ('Minimize', 'Minimize', 'Minimize XBMC', 'Minimize'),
    ('Mastermode', 'Mastermode', 'Control master mode', 'Mastermode'),
    ('TakeScreenshot', 'TakeScreenshot', 'Takes a Screenshot', 'TakeScreenshot'),
    ('ReloadSkin', 'ReloadSkin', "Reload XBMC's skin", 'ReloadSkin'),
    ('UnloadSkin', 'UnloadSkin', "Unload XBMC's skin", 'UnloadSkin'),
    ('RefreshRSS', 'RefreshRSS', 'Reload RSS feeds from RSSFeeds.xml', 'RefreshRSS'),
    ('Playlist.Clear', 'Playlist.Clear', 'Clear the current playlist', 'Playlist.Clear'),
    ('RipCD', 'RipCD', 'Rip the currently inserted audio CD', 'RipCD'),
    ('Skin.ResetSettings', 'Skin.ResetSettings', 'Resets all skin settings', 'Skin.ResetSettings'),
    ('System.LogOff', 'System.LogOff', 'Log off current user', 'System.LogOff'),
    ('Container.Refresh', 'Container.Refresh', 'Refresh current listing', 'Container.Refresh'),
    ('Container.Update', 'Container.Update', 'Update current listing. Send Container.Update(path,replace) to reset the path history', 'Container.Update'),
    ('Container.NextViewMode', 'Container.NextViewMode', 'Move to the next view type (and refresh the listing)', 'Container.NextViewMode'),
    ('Container.PreviousViewMode', 'Container.PreviousViewMode', 'Move to the previous view type (and refresh the listing)', 'Container.PreviousViewMode'),
    ('Container.NextSortMethod', 'Container.NextSortMethod', 'Change to the next sort method', 'Container.NextSortMethod'),
    ('Container.PreviousSortMethod', 'Container.PreviousSortMethod', 'Change to the previous sort method', 'Container.PreviousSortMethod'),
    ('Container.SortDirection', 'Container.SortDirection', 'Toggle the sort direction', 'Container.SortDirection'),
    ('UpdateAddonRepos', 'UpdateAddonRepos', 'Check add-on repositories for updates', 'UpdateAddonRepos'),
    ('UpdateLocalAddons', 'UpdateLocalAddons', 'Check for local add-on changes', 'UpdateLocalAddons'),
    ('ToggleDPMS', 'ToggleDPMS', 'Toggle DPMS mode manually', 'ToggleDPMS'),
    ('Weather.Refresh', 'Weather.Refresh', 'Force weather data refresh', 'Weather.Refresh'),
    ('Weather.LocationNext', 'Weather.LocationNext', 'Switch to next weather location', 'Weather.LocationNext'),
    ('Weather.LocationPrevious', 'Weather.LocationPrevious', 'Switch to previous weather location', 'Weather.LocationPrevious'),
    ('LIRC.Stop', 'LIRC.Stop', 'Removes XBMC as LIRC client', 'LIRC.Stop'),
    ('LIRC.Start', 'LIRC.Start', 'Adds XBMC as LIRC client', 'LIRC.Start'),
    ('LCD.Suspend', 'LCD.Suspend', 'Suspends LCDproc', 'LCD.Suspend'),
    ('LCD.Resume', 'LCD.Resume', 'Resumes LCDproc', 'LCD.Resume'),
)),
)

# Remote buttons handled by XBMC.  For a list of all buttons see: http://wiki.xbmc.org/?title=Keymap.xml#Remote_Section http://kodi.wiki/view/Keymap.xml#Remotes

REMOTE_BUTTONS = (
(eg.ActionGroup, "Remote", "Remote", None, (
    ("RemoteLeft", "Left", "", "left"),
    ("RemoteRight", "Right", "", "right"),
    ("RemoteUp", "Up", "", "up"),
    ("RemoteDown", "Down", "", "down"),
    ("RemoteSelect", "Select", "", "select"),
    ("RemoteBack", "Back", "", "back"),
    ("RemoteMenu", "Menu", "", "menu"),
    ("RemoteInfo", "Info", "", "info"),
    ("RemoteDisplay", "Display", "", "display"),
    ("RemoteTitle", "Title", "", "title"),
    ("RemotePlay", "Play", "", "play"),
    ("RemotePause", "Pause", "", "pause"),
    ("RemoteReverse", "Reverse", "", "reverse"),
    ("RemoteForward", "Forward", "", "forward"),
    ("RemoteSkipPlus", "Skip +", "", "skipplus"),
    ("RemoteSkipMinus", "Skip -", "", "skipminus"),
    ("RemoteStop", "Stop", "", "stop"),
    ("Remote0", "0", "", "zero"),
    ("Remote1", "1", "", "one"),
    ("Remote2", "2", "", "two"),
    ("Remote3", "3", "", "three"),
    ("Remote4", "4", "", "four"),
    ("Remote5", "5", "", "five"),
    ("Remote6", "6", "", "six"),
    ("Remote7", "7", "", "seven"),
    ("Remote8", "8", "", "eight"),
    ("Remote9", "9", "", "nine"),
    ("RemotePower", "Power", "", "power"),
    ("RemoteMyTV", "My TV", "", "mytv"),
    ("RemoteMyMusic", "My Music", "", "mymusic"),
    ("RemoteMyPictures", "My Pictures", "", "mypictures"),
    ("RemoteMyVideo", "My Video", "", "myvideo"),
    ("RemoteRecord", "Record", "", "record"),
    ("RemoteStart", "Start", "", "start"),
    ("RemoteVolPlus", "Vol +", "", "volumeplus"),
    ("RemoteVolMinus", "Vol -", "", "volumeminus"),
    ("Remotechannelplus", "CH +", "", "channelplus"),
    ("Remotechannelminus", "CH -", "", "channelminus"),
    ("Remotepageplus", "PG +", "", "pageplus"),
    ("Remotepageminus", "PG -", "", "pageminus"),
    ("RemoteMute", "Mute", "", "mute"),
    ("RemoteRecordedTV", "Recorded TV", "", "recordedtv"),
    ("RemoteLiveTV", "Live TV", "", "livetv"),
    ("RemoteStar", "*", "", "star"),
    ("Remote#", "#", "", "hash"),
    ("RemoteClear", "Clear", "", "clear"),
    ("Remoteguide", "Guide", "", "guide"),
    ("Remoteenter", "Enter", "", "enter"),
    ("Remotexbox", "Xbox", "", "xbox"),
    ("Remoteteletext", "Teletext", "", "teletext"),
    ("Remotered", "Red", "", "red"),
    ("Remotegreen", "Green", "", "green"),
    ("Remoteyellow", "Yellow", "", "yellow"),
    ("Remoteblue", "Blue", "", "blue"),
    ("Remotesubtitle", "Subtitle", "", "subtitle"),
    ("Remotelanguage", "Language", "", "language"),
)),
)
# Remote buttons handled by XBMC.  For a list of all buttons see: http://wiki.xbmc.org/?title=Keymap.xml#Gamepad_Section http://kodi.wiki/view/Keymap.xml#Gamepads

GAMEPAD_BUTTONS = (
(eg.ActionGroup, "Gamepad", "Gamepad", None, (
    ("GamepadA", "A", "", "a"),
    ("GamepadB", "B", "", "b"),
    ("GamepadX", "X", "", "x"),
    ("GamepadY", "Y", "", "y"),
    ("GamepadWhite", "White", "", "white"),
    ("GamepadBlack", "Black", "", "black"),
    ("GamepadStart", "Start", "", "start"),
    ("GamepadBack", "Back", "", "back"),
    ("GamepadLeftThumbButton", "LeftThumbButton", "", "leftthumbbutton"),
    ("GamepadRightThumbButton", "RightThumbButton", "", "rightthumbbutton"),
    ("GamepadLeftThumbStick", "LeftThumbStick", "", "leftthumbstick"),
    ("GamepadLeftThumbStickUp", "LeftThumbStickUp", "", "leftthumbstickup"),
    ("GamepadLeftThumbStickDown", "LeftThumbStickDown", "", "leftthumbstickdown"),
    ("GamepadLeftThumbStickLeft", "LeftThumbStickLeft", "", "leftthumbstickleft"),
    ("GamepadLeftThumbStickRight", "LeftThumbStickRight", "", "leftthumbstickright"),
    ("GamepadRightThumbStick", "RightThumbStick", "", "rightthumbstick"),
    ("GamepadRightThumbStickUp", "RightThumbStickUp", "", "rightthumbstickup"),
    ("GamepadRightThumbStickDown", "RightThumbStickDown", "", "rightthumbstickdown"),
    ("GamepadRightThumbStickLeft", "RightThumbStickLeft", "", "rightthumbstickleft"),
    ("GamepadRightThumbStickRight", "RightThumbStickRight", "", "rightthumbstickright"),
    ("GamepadLeftTrigger", "LeftTrigger", "", "lefttrigger"),
    ("GamepadRightTrigger", "RightTrigger", "", "righttrigger"),
    ("GamepadLeftAnalogTrigger", "LeftAnalogTrigger", "", "leftanalogtrigger"),
    ("GamepadRightAnalogTrigger", "RightAnalogTrigger", "", "rightanalogtrigger"),
    ("GamepadDpadLeft", "DpadLeft", "", "dpadleft"),
    ("GamepadDpadRight", "DpadRight", "", "dpadright"),
    ("GamepadDpadUp", "DpadUp", "", "dpadup"),
    ("GamepadDpadDown", "DpadDown", "", "dpaddown"),
)),
)

# Remote buttons handled by XBMC.  For a list of all buttons see: http://wiki.xbmc.org/?title=Keymap.xml#Custom_Joystick_Configuration http://kodi.wiki/view/Keymap.xml#Custom_Joystick_Configuration

APPLEREMOTE_BUTTONS = (
(eg.ActionGroup, "AppleRemote", "AppleRemote", None, (
    ("AppleRemote1", "plus", "AppleRemote", 1),
    ("AppleRemote2", "minus", "AppleRemote", 2),
    ("AppleRemote3", "left", "AppleRemote", 3),
    ("AppleRemote4", "right", "AppleRemote", 4),
    ("AppleRemote5", "center", "AppleRemote", 5),
    ("AppleRemote6", "menu", "AppleRemote", 6),
    ("AppleRemote7", "hold center", "AppleRemote", 7),
    ("AppleRemote8", "hold menu", "AppleRemote", 8),

    #<!-- old buttons for ATV1 <2.2, used on OSX  -->
    ("AppleRemote9", "hold left", "AppleRemote:\nold buttons for ATV1 <2.2, used on OSX", 9),
    ("AppleRemote10", "hold right", "AppleRemote:\nold buttons for ATV1 <2.2, used on OSX", 10),

    #<!-- new aluminium remote buttons  -->
    ("AppleRemote12", "play/pause", "AppleRemote:\nnew aluminium remote buttons", 12),

    #<!-- Additional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen-->
    ("AppleRemote13", "pageup", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 13),
    ("AppleRemote14", "pagedown", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 14),
    ("AppleRemote15", "pause", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 15),
    ("AppleRemote16", "play2", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 16),
    ("AppleRemote17", "stop", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 17),
    ("AppleRemote18", "fast fwd", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 18),
    ("AppleRemote19", "rewind", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 19),
    ("AppleRemote20", "skip fwd", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 20),
    ("AppleRemote21", "skip back", "AppleRemote:\nAdditional buttons via Harmony Apple TV remote profile - these are also the learned buttons on Apple TV 2gen", 21),

    #<!-- Learned remote buttons (ATV1 >2.3) -->
    ("AppleRemote70", "Play", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 70),
    ("AppleRemote71", "Pause", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 71),
    ("AppleRemote72", "Stop", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 72),
    ("AppleRemote73", "Previous", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 73),
    ("AppleRemote74", "Next", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 74),
    ("AppleRemote75", "Rewind", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 75),
    ("AppleRemote76", "Forward", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 76),
    ("AppleRemote77", "Return", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 77),
    ("AppleRemote78", "Enter", "AppleRemote:\nLearned remote buttons (ATV1 >2.3)", 78),

    #<!-- few gestures from Apple's iPhone Remote (ATV1 > 2.3 ?) -->
    ("AppleRemote80", "SwipeLeft", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 80),
    ("AppleRemote81", "SwipeRight", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 81),
    ("AppleRemote82", "SwipeUp", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 82),
    ("AppleRemote83", "SwipeDown", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 83),

    ("AppleRemote85", "FlickLeft", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 85),
    ("AppleRemote86", "FlickRight", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 86),
    ("AppleRemote87", "FlickUp", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 87),
    ("AppleRemote88", "FlickDown", "AppleRemote:\nfew gestures from Apple's iPhone Remote (ATV1 > 2.3 ?)", 88),
)),
)

# Keyboard keys handled by XBMC.  For a list of all keys see: http://wiki.xbmc.org/index.php?title=List_of_XBMC_keynames http://kodi.wiki/view/List_of_XBMC_keynames

KEYBOARD_KEYS = (
(eg.ActionGroup, "Keyboard", "Keyboard", None, (
    ("KeyboardBackspace", "Backspace", "", "backspace"),
    ("KeyboardEnter", "Enter", "", "enter"),
    ("KeyboardTab", "Tab", "", "tab"),
)),
)

# Support functions
def ParseString2(text, filterFunc=None):
    start = 0
    chunks = []
    last = len(text) - 1
    while 1:
        pos = text.find('{{', start)
        if pos < 0:
            break
        if pos == last:
            break
        chunks.append(text[start:pos])
        start = pos + 2
        end = text.find('}}', start)
        if end == -1:
            raise SyntaxError("unmatched bracket")
        word = text[start:end]
        res = None
        if filterFunc:
            res = filterFunc(word)
        if res is None:
            res = eval(word, {}, eg.globals.__dict__)
        chunks.append(unicode(res))
        start = end + 2
    chunks.append(text[start:])
    return "".join(chunks)


class ActionPrototype(eg.ActionClass):
    def __call__(self):
        try:
            self.plugin.xbmc.send_action(self.value, ACTION_BUTTON)
        except:
            raise self.Exceptions.ProgramNotRunning

# actions handled by XBMC.  For a list of all actions see: http://xbmc.org/wiki/?title=Action_IDs#General_actions_available_throughout_most_of_XBMC
"""
CONFIGURABLE_ACTIONS = (
(eg.ActionGroup, "General", "General", None, (
    ("UpdateLibrary", "UpdateLibrary", "UpdateLibrary", "UpdateLibrary(Video)"),
)),
)
"""
class UpdateLibrary(eg.ActionBase):
    def __call__(self, libraryType="Video", updatePath=""):
        parameterString = libraryType
        if not updatePath == "":
                    parameterString += "," + updatePath
        print repr(parameterString)
        try:
            #self.plugin.xbmc.send_action("UpdateLibrary("+libraryType+","+updatePath+")", ACTION_BUTTON)
            #self.plugin.xbmc.send_action("UpdateLibrary(video,\\\\MYTHTV\\Media\\Multimedia\\Movies\\Anime TV\\Mikagura Gakuen Kumikyoku)", ACTION_BUTTON)
            self.plugin.xbmc.send_action("UpdateLibrary(" + str(parameterString) + ")")
        except:
            raise self.Exceptions.ProgramNotRunning

    def Configure(self, libraryType="Video", updatePath="" ):
        panel = eg.ConfigPanel()
        textControl1 = wx.TextCtrl(panel, -1, libraryType)
        textControl2 = wx.TextCtrl(panel, -1, updatePath)
        panel.sizer.Add(textControl1, 1, wx.EXPAND)
        panel.sizer.Add(textControl2, 1, wx.EXPAND)
        while panel.Affirmed():
            panel.SetResult(textControl1.GetValue(), textControl2.GetValue())

class BuiltInFunctions(eg.ActionBase):
    def __call__(self, Function="Help", Parameters=""):
        try:
            self.plugin.xbmc.send_action(Function + "(" + str(eg.ParseString(Parameters)) + ")")
        except:
            raise self.Exceptions.ProgramNotRunning

    def Configure(self, Function="Help", Parameters=""):
        import os, contextlib, pickle
        BuiltInFunctionList = {}
        def UpdateFunctions():
            def GetFunctions():
                try:
                    with contextlib.closing(urllib2.urlopen(urllib2.Request('https://raw.githubusercontent.com/xbmc/xbmc/Isengard/xbmc/interfaces/Builtins.cpp'))) as Builtinscpp:
                        Builtinslines = Builtinscpp.read().splitlines(False)
                except (urllib2.HTTPError, urllib2.URLError):
                    eg.PrintError('XBMC2: Error: Can\'t connect to "https://raw.githubusercontent.com/xbmc/xbmc/Isengard/xbmc/interfaces/Builtins.cpp" to update "BuiltInFunctions".')
                    raise
                else:
                    Builtinslist = Builtinslines[Builtinslines.index("const BUILT_IN commands[] = {") + 1:Builtinslines.index("};")]

                    FunctionList = []
                    for Function in Builtinslist:
                        try:
                            FunctionList.append((Function.strip(' {},').split(',')[0].strip('" '), True if Function.strip(' {},').split(',')[1].strip('" ') == 'true' else False, Function.strip(' {},').split(',')[2].strip('" ')))
                        except IndexError:
                            pass
                    return FunctionList
            def GetSyntax():
                def XMLText(Node):
                    text = ''
                    #print "Info:", Node.nodeValue, Node.nodeName
                    try:
                        for n in Node.childNodes:
                            text += n.nodeValue if n.nodeName == '#text' else XMLText(n)
                        #print "Text:", text
                    except:
                        print "Try:", Node.nodeValue, Node.nodeName

                    return text

                URL = 'http://kodi.wiki/view/List_of_built-in_functions'
                UserAgent = 'XBMC2 EventGhost plugin'
                hdr = {'User-Agent': UserAgent}

                request = urllib2.Request(URL, headers=hdr)
                try:
                    w = urllib2.urlopen(request)
                except (urllib2.HTTPError, urllib2.URLError):
                    eg.PrintError('XBMC2: Error: Can\'t connect to "http://kodi.wiki/view/List_of_built-in_functions" to update "BuiltInFunctions".')
                    raise
                else:
                    Page2 = w.read()

                    ActionDict = {}
                    for table in xml.dom.minidom.parseString(Page2).getElementsByTagName("table")[1:2]:
                        for tr in table.getElementsByTagName("tr"):
                            for code in tr.getElementsByTagName("code")[0:1]:
                                ActionDict[XMLText(code).strip().split('(')[0]] = (XMLText(code).strip(), XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                #print XMLText(code).strip().split('(')[0]
                    return ActionDict

            try:
                FunctionsList = GetFunctions()
            except (urllib2.HTTPError, urllib2.URLError):
                BuiltInFunctionList['Help'] = {'Syntax': 'Help', 'Description': "", 'Parameters': False}
            else:
                try:
                    SyntaxList = GetSyntax()
                except (urllib2.HTTPError, urllib2.URLError):
                    SyntaxList = {}
                for Function, Parameters, Description in sorted(FunctionsList):
                    try:
                        SyntaxList[Function]
                        Syntax = SyntaxList[Function][0]
                        Description = SyntaxList[Function][1]
                    except KeyError:
                        Syntax = Function
                    BuiltInFunctionList[Function] = {'Syntax': Syntax, 'Description': Description, 'Parameters': Parameters}

                """
                if not os.path.exists(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2')):
                    os.makedirs(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2'))
                with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'BuiltInFunctions.dat'), 'wb') as f:
                    pickle.dump(BuiltInFunctionList, f, 1)
                    print 'XBMC2: Builtin functions updated.'
                """
                try:
                    writeData('BuiltInFunctions.dat', BuiltInFunctionList)
                except IOError:
                    pass
                else:
                    print 'XBMC2: Builtin functions updated.'

        """
        def loadfile():
            try:
                with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'BuiltInFunctions.dat'), 'rb') as f:
                    return pickle.load(f)
            except IOError:
                print 'XBMC2: Warning: Failed to open: BuiltInFunctions.dat'
                raise
        """
        def OnUpdate(event):
            UpdateFunctions()

        def OnFunctionChange(event):
            if event.GetEventObject() == panel.combo_box_function:
                if not BuiltInFunctionList[panel.combo_box_function.GetValue()]['Parameters']:
                    panel.text_ctrl_parameters.Disable()
                else:
                    panel.text_ctrl_parameters.Enable()
                panel.text_ctrl_syntax.SetValue(BuiltInFunctionList[panel.combo_box_function.GetValue()]['Syntax'])
                panel.label_description.SetLabel(BuiltInFunctionList[panel.combo_box_function.GetValue()]['Description'])

        def initPanel(self):
            self.combo_box_function = wx.ComboBox(self, wx.ID_ANY, value=Function, choices=sorted(BuiltInFunctionList.keys()), style=wx.CB_READONLY)
            self.sizer_function_staticbox = wx.StaticBox(self, wx.ID_ANY, "Function")
            self.label_left = wx.StaticText(self, wx.ID_ANY, "(")
            self.text_ctrl_parameters = wx.TextCtrl(self, wx.ID_ANY, Parameters)
            self.label_right = wx.StaticText(self, wx.ID_ANY, ")")
            self.sizer_parameter_staticbox = wx.StaticBox(self, wx.ID_ANY, "Parameter(s)")
            self.text_ctrl_syntax = wx.TextCtrl(self, wx.ID_ANY, BuiltInFunctionList[Function]['Syntax'], style=wx.TE_READONLY | wx.BORDER_NONE)
            self.sizer_syntax_staticbox = wx.StaticBox(self, wx.ID_ANY, "Syntax")
            self.label_description = wx.TextCtrl(self, wx.ID_ANY, BuiltInFunctionList[Function]['Description'], style=wx.TE_MULTILINE | wx.TE_READONLY | wx.BORDER_NONE | wx.TE_RICH)
            self.sizer_description_staticbox = wx.StaticBox(self, wx.ID_ANY, "Description")
            self.button_update = wx.Button(self, wx.ID_ANY, "Update")
            if not BuiltInFunctionList[Function]['Parameters']:
                self.text_ctrl_parameters.Disable()

            setPanelProperties(self)
            doPanelLayout(self)

            self.Bind(wx.EVT_COMBOBOX, OnFunctionChange, self.combo_box_function)
            self.button_update.Bind(wx.EVT_BUTTON, OnUpdate)

        def setPanelProperties(self):
            self.label_left.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, ""))
            self.label_right.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, ""))
            self.button_update.SetToolTipString("Update \"BuiltInFunctions\" form Kodis website.")

        def doPanelLayout(self):
            sizer_main = wx.BoxSizer(wx.VERTICAL)
            self.sizer_description_staticbox.Lower()
            sizer_description = wx.StaticBoxSizer(self.sizer_description_staticbox, wx.HORIZONTAL)
            self.sizer_syntax_staticbox.Lower()
            sizer_syntax = wx.StaticBoxSizer(self.sizer_syntax_staticbox, wx.HORIZONTAL)
            sizer_functionparameter = wx.BoxSizer(wx.HORIZONTAL)
            self.sizer_parameter_staticbox.Lower()
            sizer_parameter = wx.StaticBoxSizer(self.sizer_parameter_staticbox, wx.HORIZONTAL)
            self.sizer_function_staticbox.Lower()
            sizer_function = wx.StaticBoxSizer(self.sizer_function_staticbox, wx.HORIZONTAL)
            sizer_function.Add(self.combo_box_function, 0, 0, 0)
            sizer_functionparameter.Add(sizer_function, 0, 0, 0)
            sizer_parameter.Add(self.label_left, 0, 0, 0)
            sizer_parameter.Add(self.text_ctrl_parameters, 1, wx.EXPAND, 0)
            sizer_parameter.Add(self.label_right, 0, 0, 0)
            sizer_functionparameter.Add(sizer_parameter, 1, 0, 0)
            sizer_main.Add(sizer_functionparameter, 0, wx.EXPAND, 0)
            sizer_syntax.Add(self.text_ctrl_syntax, 1, 0, 0)
            sizer_main.Add(sizer_syntax, 0, wx.EXPAND, 0)
            sizer_description.Add(self.label_description, 1, wx.EXPAND, 0)
            sizer_main.Add(sizer_description, 1, wx.EXPAND, 0)
            sizer_main.Add(self.button_update, 0, wx.ALIGN_RIGHT, 0)
            self.sizer.Add(sizer_main, 1, wx.EXPAND, 0)

        panel = eg.ConfigPanel()
        try:
            #BuiltInFunctionList = loadfile()
            BuiltInFunctionList = readData('BuiltInFunctions.dat')
        except IOError:
            UpdateFunctions()

        initPanel(panel)

        while panel.Affirmed():
            panel.SetResult(str(panel.combo_box_function.GetValue()), str(panel.text_ctrl_parameters.GetValue()))

class ButtonPrototype(eg.ActionClass):
    def __call__(self):
        try:
            packet = PacketBUTTON(map_name=str("R1"), button_name=str(self.value), repeat=0)
            packet.send(self.plugin.xbmc.sock, self.plugin.xbmc.addr, self.plugin.xbmc.uid)
        except:
            raise self.Exceptions.ProgramNotRunning

class GamepadPrototype(eg.ActionClass):
    def __call__(self):
        try:
            packet = PacketBUTTON(map_name=str("XG"), button_name=str(self.value), repeat=0)
            packet.send(self.plugin.xbmc.sock, self.plugin.xbmc.addr, self.plugin.xbmc.uid)
        except:
            raise self.Exceptions.ProgramNotRunning

class AppleRemotePrototype(eg.ActionClass):
    def __call__(self):
        try:
            packet = PacketBUTTON(map_name=str("JS0:AppleRemote"), code=self.value, repeat=0)
            packet.send(self.plugin.xbmc.sock, self.plugin.xbmc.addr, self.plugin.xbmc.uid)
        except:
            raise self.Exceptions.ProgramNotRunning

class KeyboardPrototype(eg.ActionClass):
    def __call__(self):
        try:
            packet = PacketBUTTON(map_name=str("KB"), button_name=str(self.value), repeat=0)
            packet.send(self.plugin.xbmc.sock, self.plugin.xbmc.addr, self.plugin.xbmc.uid)
        except:
            raise self.Exceptions.ProgramNotRunning

class XBMC_HTTP_API:

    def __init__(self):
        pass

    def connect(self, ip="127.0.0.1", port="80", username='', password=''):
        self.ip = ip
        self.port = port
        self.base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
        print 'HTTP API connected'

    def send(self, method, params = ""):
        request = urllib2.Request('http://'+self.ip+':'+self.port+'/xbmcCmds/xbmcHttp?command='+method+'('+urllib2.quote(eg.ParseString(params), ':\\')+')')
        request.add_header("Authorization", "Basic %s" % self.base64string)
        try:
            responce = urllib2.urlopen(request).readlines()
        except IOError:
            #eg.PrintError('HTTP API connection error:'+' http://'+self.ip+':'+self.port+'\n'+method+'('+urllib2.quote(eg.ParseString(params), ':\\')+')')
            raise
        else:
            if (''.join(responce).find('<html>') != -1):
                responce2 = {}
                for lines in responce:
                    if (lines.find('<html>') != -1): lines = lines[lines.find('<html>')+6:]
                    if (lines.find('</html>') != -1): lines = lines[:lines.find('</html>')]
                    if (lines.find('<li>') != -1):
                        if (lines.find('OK') != -1):
                            responce2 = 'OK'
                        elif (lines.find('ERROR') != -1):
                            responce2 = lines[4:].rstrip('\n').split(':', 1)
                        elif (lines.find(':') != -1):
                            lines = lines[4:].rstrip('\n').split(':', 1)
                            responce2[lines[0]] = lines[1]
                        else:
                            responce2 = lines[4:].rstrip('\n')
                    else:
                        if (lines.rstrip('\n') != ''):
                            responce2 = lines.rstrip('\n')
                return responce2

    def close(self):
        print 'HTTP API connection closed'

class XBMC_JSON_RPC:

    def __init__(self):
        self.jsoninit = {'jsonrpc':'2.0', 'id':1}

    def connect(self, ip="127.0.0.1", port=80, username='', password=''):
        self.ip = ip
        self.port = str(port)
        self.base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
        print 'JSON-RPC connected'

    def send(self, method, params = None, wait=True):
        self.jsoninit['method'] = method
        if params:
            self.jsoninit['params'] = params
        else:
            if self.jsoninit.has_key('params'):
                del self.jsoninit['params']
        request = urllib2.Request('http://'+self.ip+':'+self.port+'/jsonrpc',json.dumps(self.jsoninit))
        request.add_header("Authorization", "Basic %s" % self.base64string)
        request.add_header('Content-Type', 'application/json')
        try:
            #responce = urllib2.urlopen(request, timeout=5).read()
            responce = urllib2.urlopen(request, timeout=(60 if wait else 1)).read()
        except urllib2.HTTPError as e:
            #print 'HTTPError', e.args
            #if hasattr(e, 'reason'): # <--
            #        print 'We failed to reach a server.'
            #        print 'Reason: ', e.reason
            #if hasattr(e, 'code'): # <--
            #        print 'The server couldn\'t fulfill the request.'
            #        import BaseHTTPServer
            #        print 'Error code: ', e.code, BaseHTTPServer.BaseHTTPRequestHandler.responses[e.code]
            raise
        except urllib2.URLError as e:
            #print 'URLError', e.reason, e.args
            if type(e.reason) == socket.timeout:
                return {'noresult': None}
            else:
                #print 'URLError', e.reason, e.args
                raise
      #URLError: <urlopen error timed out>
      #URLError timed out (timeout('timed out',),)
      #URLError <class 'socket.timeout'> (timeout('timed out',),)
        except:
            #eg.PrintError('JSON-RPC connect error: ')
            #import sys, traceback
            #traceback.print_exc()
            raise
        #except IOError:
        #eg.PrintError('JSON-RPC connection error:'+' http://'+self.ip+':'+self.port+'\n'+json.dumps(self.jsoninit))
        else:
            try:
                #print responce
                return json.loads(responce)
            except ValueError as e:
                #eg.PrintError("Server responded but didn't provide valid JSON data: ")
                #import sys, traceback
                #traceback.print_exc()
                #eg.PrintError("Error data: " + str(e)+': "'+str(responce)+'"')
                raise

    def close(self):
        print 'JSON-RPC connection closed'

class GetCurrentlyPlayingFilename(eg.ActionClass):
  description = "Get filename of currently playing file"

  def __call__(self):
        responce = self.plugin.JSON_RPC.send('Player.GetActivePlayers')
        if (responce != None):
            Method = None
            if (responce['result']['picture']): Method = 'Picture'
            elif (responce['result']['video']): Method = 'Video'
            elif (responce['result']['audio']): Method = 'Audio'
            if Method:
                print 'Method: ', Method
                if (Method != 'Picture'):
                    responce = self.plugin.JSON_RPC.send(Method+'Playlist.GetItems')
#                    print 'eg.result: ', responce['items'][responce['current']]['file']
                    return responce['result']['items'][responce['result']['current']]['file']
                else:
                    responce = self.plugin.HTTP_API.send('getcurrentlyplaying')
                    if responce:
                        if (responce['result']['Filename'] == ''):
                            print 'No file playing'
                        return responce['result']['Filename']
                    else:
                        raise self.Exceptions.ProgramNotRunning
            else:
                print 'No file playing'
        else:
            responce = self.plugin.HTTP_API.send('getcurrentlyplaying')
            if responce:
                if (responce['Filename'] == ''):
                    print 'No file playing'
                return responce['Filename']
            else:
                raise self.Exceptions.ProgramNotRunning

class SendNotification(eg.ActionClass):
    description = "Send a notification to the connected XBMC"

    def __call__(self, title, message):
        try:
            self.plugin.xbmc.send_notification(str(eg.ParseString(title)), str(eg.ParseString(message)))
        except UnicodeEncodeError:
#            print "Error: ascii charecters only."
            eg.PrintError("Error: ascii charecters only.")
        except:
            raise self.Exceptions.ProgramNotRunning
    def Configure(self, title='Hello', message='world'):
        panel = eg.ConfigPanel()
        Title = wx.TextCtrl(panel, -1, value=title)
        Message = wx.TextCtrl(panel, -1, value=message)
        panel.sizer.Add(wx.StaticText(panel, -1, "Title"))
        panel.sizer.Add(Title)
        panel.sizer.Add(wx.StaticText(panel, -1, "Message"))
        panel.sizer.Add(Message)
        while panel.Affirmed():
            panel.SetResult(Title.GetValue(), Message.GetValue())

class HTTPAPI(eg.ActionClass):
    description = "Run any <a href='http://wiki.xbmc.org/index.php?title=Web_Server_HTTP_API'>XBMC HTTP API</a> command."

    def __call__(self, command, param, category, log):
        if param:
            responce = self.plugin.HTTP_API.send(command, param)
        else:
            responce = self.plugin.HTTP_API.send(command)
        if responce != None:
#            print 'Result:\n', responce
            if log:
                import pprint
                print 'Result:'
                pprint.PrettyPrinter(indent=2).pprint(responce)
            return responce
        else:
            raise self.Exceptions.ProgramNotRunning

    def Configure(self, command="GetCurrentPlaylist", param="", category=0, log=True):
        class record:
            pass
        httpapi = record()
        httpapi.Headers = []
        httpapi.Commands = []
        OldCategory = category

        def OnUpdate(event):
            UpdateCommands()
            """
            try:
                with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'httpapi.dat'), 'rb') as f:
                    import pickle
                    httpapi.Headers, httpapi.Commands = pickle.load(f)
            except IOError:
#                print 'Failed to open: httpapi.dat'
                eg.PrintError('Failed to open: httpapi.dat')
            """
            try:
                httpapi.Headers, httpapi.Commands = readData('httpapi.dat')
            except IOError:
                pass
            else:
                category = OldCategory
                HBoxControl.Clear()
                for i in httpapi.Headers:
                    HBoxControl.Append(i)
                HBoxControl.SetValue(httpapi.Headers[category])
                UpdateCommandCtrl(HBoxControl.GetSelection())
        def OnCommandChange(event):
            if event.GetEventObject() == comboBoxControl:
                syntax.SetLabel(httpapi.Commands[HBoxControl.GetSelection()][1][event.GetSelection()])
                description.SetLabel(httpapi.Commands[HBoxControl.GetSelection()][2][event.GetSelection()])
                description.Wrap(480)
            else:
                UpdateCommandCtrl(event.GetSelection())
        def UpdateCommandCtrl(Selection):
            value = comboBoxControl.GetValue()
            comboBoxControl.Clear()
            for i in httpapi.Commands[Selection][0]:
                comboBoxControl.Append(i)
            comboBoxControl.SetValue(value)

        def GetText(nodes):
            Text = ''
            for node in nodes.childNodes:
                if node.nodeType == Node.TEXT_NODE: Text += node.data
                else: Text += GetText(node)
            return Text
        def UpdateCommands():
            httpapi.Headers = [];httpapi.Commands = []

            try:
                doc = xml.dom.minidom.parseString(urllib2.urlopen(urllib2.Request('http://kodi.wiki/view/Web_Server_HTTP_API')).read())
            except (urllib2.HTTPError, urllib2.URLError):
                print "Connect error"
            else:
                for h3 in doc.getElementsByTagName("h3")[10:-1]:
                    for span in h3.getElementsByTagName("span"):
                        httpapi.Headers.append(span.childNodes[0].data)
                Header = 0
                for node in doc.getElementsByTagName("table")[3:9]:
                    for node2 in node.getElementsByTagName("tr")[1:]:
                        httpapi.Commands.append([[],[],[]])
                        node3 = node2.getElementsByTagName("td")[0]
                        for node4 in node3.childNodes:
                            if node4.nodeType == Node.TEXT_NODE:
                                Text = node4.data.strip()
                                httpapi.Commands[Header][1].append(Text)
                                Pos = Text.find('(')
                                if (Pos != -1):
                                    httpapi.Commands[Header][0].append(Text[:Pos])
                                else:
                                    httpapi.Commands[Header][0].append(Text)
                            else:
                                print '<'+node4.tagName+'>'
                        httpapi.Commands[Header][2].append(GetText(node2.getElementsByTagName("td")[1]).strip())
                    Header += 1
                try:
                    writeData('httpapi.dat', (httpapi.Headers, httpapi.Commands))
                except IOError:
                    pass

        import os
        try:
            httpapi.Headers, httpapi.Commands = readData('httpapi.dat')
        except IOError:
            UpdateCommands()
            #category = 0
            #httpapi.Headers = ['No categorys']
            #httpapi.Commands = [[['No commands'],[''],['']]]
        panel = eg.ConfigPanel()
        HBoxControl = wx.ComboBox(panel, -1, value=httpapi.Headers[category], choices=httpapi.Headers, style=wx.CB_READONLY)
        comboBoxControl = wx.ComboBox(panel, -1, value=command, choices=httpapi.Commands[category][0])
        comboBoxControl.SetStringSelection(command)
        textControl1 = wx.TextCtrl(panel, -1, param, size=(500, -1))
        Category = wx.BoxSizer(wx.HORIZONTAL)
        Category.Add(wx.StaticText(panel, -1, "Category"))
        Category.Add(HBoxControl)
        Category.Add(wx.StaticText(panel, -1, "Command"))
        Category.Add(comboBoxControl)
        panel.sizer.Add(wx.StaticText(panel, -1, "Choose or type in a HTTP API command and add parameter(s)"))
        panel.sizer.Add(Category)
        panel.sizer.Add(textControl1)
        panel.sizer.Add(wx.StaticText(panel, -1, "Command syntax:"))
        if (comboBoxControl.GetSelection() != -1):
            syntax = wx.TextCtrl(panel, -1, httpapi.Commands[category][1][comboBoxControl.GetSelection()], (1, 70), size=(500,-1), style=wx.TE_READONLY)
        else:
            syntax = wx.TextCtrl(panel, -1, '', (1, 70), size=(500,-1), style=wx.TE_READONLY)
        panel.sizer.Add(syntax)
        panel.sizer.Add(wx.StaticBox(panel, -1, 'Command description:', size=(500, 150)))
        if (comboBoxControl.GetSelection() != -1):
            description = wx.StaticText(panel, -1, httpapi.Commands[category][2][comboBoxControl.GetSelection()], (5, 105), style=wx.ALIGN_LEFT)
        else:
            description = wx.StaticText(panel, -1, '', (5, 105), style=wx.ALIGN_LEFT)
        description.Wrap(480)
        CheckBox = wx.CheckBox(panel, -1, 'Show result in the log')
        CheckBox.SetValue(log)
        UpdateButton = wx.Button(panel, -1, 'Update')
        UpdateButton.Bind(wx.EVT_BUTTON, OnUpdate)
        Bottom = wx.BoxSizer(wx.HORIZONTAL)
        Bottom.Add(CheckBox)
        Bottom.Add(UpdateButton,0,wx.LEFT,280)
        panel.sizer.Add(Bottom)
        panel.Bind(wx.EVT_COMBOBOX, OnCommandChange)
        while panel.Affirmed():
            panel.SetResult(comboBoxControl.GetValue(), textControl1.GetValue(), HBoxControl.GetSelection(), CheckBox.GetValue())

def readData(filename):
    try:
        with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', filename), 'rb') as f:
            print "Reading:", filename
            return pickle.load(f)
    except IOError:
        #eg.PrintError('XBMC2: Error opening: ' + filename)
        raise

def writeData(filename, data):
    if not os.path.exists(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2')):
        os.makedirs(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2'))
    try:
        with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', filename), 'wb') as f:
            print "Writing:", filename
            pickle.dump(data, f, 1)
    except IOError:
        eg.PrintError('XBMC2: Error writing to:', filename)
        raise

class JSONRPC(eg.ActionClass):
    description = "Run any <a href='http://wiki.xbmc.org/index.php?title=JSON_RPC'>XBMC JSON-RPC</a> method"

    def __call__(self, method="JSONRPC.Introspect", param="", log=True, wait=True):
        if param:
            responce = self.plugin.JSON_RPC.send(method, ast.literal_eval(ParseString2(param)), wait=wait)
        else:
            responce = self.plugin.JSON_RPC.send(method, wait=wait)
        if responce != None:
            if responce.has_key('noresult'):
                return
            elif responce.has_key('result'):
                if log:
                    print 'Result:\n', json.dumps(responce['result'], sort_keys=True, indent=2)
                return responce['result']
            elif responce.has_key('error'):
#                print 'Error:\n', json.dumps(responce['error'], sort_keys=True, indent=2)
                eg.PrintError('Error:\n', json.dumps(responce['error'], sort_keys=True, indent=2))
            else:
#                print 'Got bad JSON-RPC responce', responce
                eg.PrintError('Got bad JSON-RPC responce', responce)
        else:
            raise self.Exceptions.ProgramNotRunning

    def Configure(self, method="JSONRPC.Introspect", param="", log=True, wait=True):
        class record:
            Namespaces = ['No namespaces']
            Methods = {'No namespaces':['No methods']}
            Descriptions = {'No namespaces':['']}
        jsonrpc = record()
        def OnUpdate(event):
            UpdateMethods()
            try:
                jsonrpc.Namespaces, jsonrpc.Methods, jsonrpc.Descriptions = readData('jsonrpc.dat')
            except IOError:
                pass
            else:
                HBoxControl.Clear()
                for i in jsonrpc.Namespaces:
                    HBoxControl.Append(i)
                HBoxControl.SetValue(method[:method.find('.')])
                UpdateMethodCtrl(HBoxControl.GetSelection())

        def UpdateMethods():
            responce = self.plugin.JSON_RPC.send('JSONRPC.Version')
            if responce:
                jsonrpc.Namespaces = []
                jsonrpc.Methods = {}
                jsonrpc.Descriptions = {}
                if responce['result']['version'] > 2:
                    responce = self.plugin.JSON_RPC.send('JSONRPC.Introspect', json.loads('{"filterbytransport": false}'))
                    if responce != None:
                        if responce.has_key('result'):
                            for method in responce['result']['methods']:
                                namespace = method[:method.find('.')]
                                if namespace not in jsonrpc.Namespaces:
                                    jsonrpc.Namespaces.append(namespace)
                                    jsonrpc.Methods[namespace] = []
                                    jsonrpc.Descriptions[namespace] = []
                                jsonrpc.Methods[namespace].append(method[method.find('.')+1:])
                                if responce['result']['methods'][method].has_key('description'):
                                    jsonrpc.Descriptions[namespace].append(responce['result']['methods'][method]['description'])
                                else:
                                    jsonrpc.Descriptions[namespace].append('')
                            try:
                                writeData('jsonrpc.dat', (jsonrpc.Namespaces, jsonrpc.Methods, jsonrpc.Descriptions))
                            except IOError:
                                pass
                            return False
                        elif responce.has_key('error'):
#                    print 'Error', responce['error']
                            eg.PrintError('Error', responce['error'])
                            return responce['error']
                        else:
#                    print 'Got bad JSON-RPC responce', responce
                            eg.PrintError('Got bad JSON-RPC responce', responce)
                            return False
                    else:
                        return False
                else:
                    responce = self.plugin.JSON_RPC.send('JSONRPC.Introspect', json.loads('{"getdescriptions": true, "getpermissions": false}'))
                    if responce != None:
                        if responce.has_key('result'):
                            for method in responce['result']['commands']:
                                namespace = method['command'][:method['command'].find('.')]
                                if namespace not in jsonrpc.Namespaces:
                                    jsonrpc.Namespaces.append(namespace)
                                    jsonrpc.Methods[namespace] = []
                                    jsonrpc.Descriptions[namespace] = []
                                jsonrpc.Methods[namespace].append(method['command'][method['command'].find('.')+1:])
                                jsonrpc.Descriptions[namespace].append(method['description'])
                            try:
                                writeData('jsonrpc.dat', (jsonrpc.Namespaces, jsonrpc.Methods, jsonrpc.Descriptions))
                            except IOError:
                                pass
                            return False
                        elif responce.has_key('error'):
#                    print 'Error', responce['error']
                            eg.PrintError('Error', responce['error'])
                            return responce['error']
                        else:
#                    print 'Got bad JSON-RPC responce', responce
                            eg.PrintError('Got bad JSON-RPC responce', responce)
                            return False
                    else:
                        return False

        def UpdateMethodCtrl(Selection):
            comboBoxControl.Clear()
            for i in jsonrpc.Methods[jsonrpc.Namespaces[Selection]]:
                comboBoxControl.Append(i)
            comboBoxControl.SetValue(method[method.find('.')+1:])
        def OnMethodChange(event):
            if event.GetEventObject() == comboBoxControl:
                description.SetLabel(jsonrpc.Descriptions[jsonrpc.Namespaces[HBoxControl.GetSelection()]][event.GetSelection()])
                description.Wrap(480)
            else:
                UpdateMethodCtrl(event.GetSelection())
#                comboBoxControl.Clear()
#                for i in jsonrpc.Methods[jsonrpc.Namespaces[event.GetSelection()]]:
#                    comboBoxControl.Append(i)

        panel = eg.ConfigPanel()
        try:
            jsonrpc.Namespaces, jsonrpc.Methods, jsonrpc.Descriptions = readData('jsonrpc.dat')
        except IOError:
            UpdateMethods()
        HBoxControl = wx.ComboBox(panel, -1, value=method[:method.find('.')], choices=jsonrpc.Namespaces, style=wx.CB_READONLY)
        comboBoxControl = wx.ComboBox(panel, -1, value=method[method.find('.')+1:], choices=jsonrpc.Methods[jsonrpc.Namespaces[HBoxControl.GetSelection()]] , style=wx.CB_READONLY)
        textControl2 = wx.TextCtrl(panel, -1, param, size=(500, -1))
        Category = wx.BoxSizer(wx.HORIZONTAL)
        Category.Add(wx.StaticText(panel, -1, "Namespace"))
        Category.Add(HBoxControl)
        Category.Add(wx.StaticText(panel, -1, "Method"))
        Category.Add(comboBoxControl)
        panel.sizer.Add(wx.StaticText(panel, -1, "Choose a JSON-RPC Method and add any parameter(s)"))
        panel.sizer.Add(Category)
        panel.sizer.Add(textControl2)
        panel.sizer.Add(wx.StaticBox(panel, -1, 'Method description:', size=(500, 150)))
        if (comboBoxControl.GetSelection() != -1):
            description = wx.StaticText(panel, -1, jsonrpc.Descriptions[jsonrpc.Namespaces[HBoxControl.GetSelection()]][comboBoxControl.GetSelection()], (5, 70), style=wx.ALIGN_LEFT)
        else:
            description = wx.StaticText(panel, -1, '', (5, 70), style=wx.ALIGN_LEFT)
        description.Wrap(480)
        Bottom = wx.BoxSizer(wx.HORIZONTAL)
        CheckBox = wx.CheckBox(panel, -1, 'Show result in the log')
        CheckBox2 = wx.CheckBox(panel, -1, "Wait for result.(Timeout 60s)")
        CheckBox.SetValue(log)
        CheckBox2.SetValue(wait)
        Bottom.Add(CheckBox2)
        Bottom.Add(CheckBox)
        UpdateButton = wx.Button(panel, -1, 'Update')
        UpdateButton.Bind(wx.EVT_BUTTON, OnUpdate)
        Bottom.Add((0, 0), 1, wx.EXPAND)
        #Bottom.Add(UpdateButton,0,wx.LEFT,280)
        Bottom.Add(UpdateButton, flag= wx.ALIGN_RIGHT)
        panel.sizer.Add(Bottom, 1, flag=wx.EXPAND)
        panel.Bind(wx.EVT_COMBOBOX, OnMethodChange)
        while panel.Affirmed():
            try:
                jsonTemp = json.loads(textControl2.GetValue())
            except:
                pass
            else:
                if 'jsonrpc' in jsonTemp:
                    namespaceTemp, methodTemp = jsonTemp['method'].split('.')
                    HBoxControl.SetValue(namespaceTemp)
                    comboBoxControl.Clear()
                    for i in jsonrpc.Methods[jsonrpc.Namespaces[HBoxControl.GetSelection()]]:
                        comboBoxControl.Append(i)
                    comboBoxControl.SetValue(methodTemp)
                    try:
                        textControl2.SetValue(json.dumps(jsonTemp['params']))
                    except:
                        textControl2.SetValue('')

            panel.SetResult(HBoxControl.GetValue()+'.'+comboBoxControl.GetValue(), textControl2.GetValue(), CheckBox.GetValue(), CheckBox2.GetValue())

#class StopRepeating(eg.ActionClass):
#    name = "Stop Repeating"
#    description = "Stops a button repeating."

#    def __call__(self):
#        try:
#            self.plugin.xbmc.release_button()
#        except:
#            raise self.Exceptions.ProgramNotRunning

def ssdpSearch():
    import socket
    from urlparse import urlparse
    import os
    def Headers(data):
        headers = {}
        for line in data.splitlines():
            if not line.split(':', 1)[0]:
                continue
            try:
                headers[line.split(':', 1)[0].upper()] = line.split(':', 1)[1]
            except:
                headers['Start-line'] = line.split(':', 1)[0]
        return headers

    MCAST_GRP = '239.255.255.250'
    MCAST_PORT = 1900
    LIB_ID = 'upnp'
    DISCOVERY_MSG = ('M-SEARCH * HTTP/1.1\r\n' +
                                    'ST: %(library)s:%(service)s\r\n' +
                                    'MX: 3\r\n' +
                                    'MAN: "ssdp:discover"\r\n' +
                                    'HOST: 239.255.255.250:1900\r\n\r\n')

    def interface_addresses(family=socket.AF_INET):
            for fam, _, _, _, sockaddr in socket.getaddrinfo('', None):
                    if family == fam:
                            yield sockaddr[0]

    msg = DISCOVERY_MSG % dict(service='rootdevice', library=LIB_ID)
    #socket.setdefaulttimeout(3)
    USNCache = []
    ssdpResultList = []
    XBMCResultList = {}
    for addr in interface_addresses():
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
        sock.settimeout(3)
        sock.bind((addr, 0))

        for _ in xrange(2):
            sock.sendto(msg, (MCAST_GRP, MCAST_PORT))

        while True:
            try:
                #data = sock.recv(1024).splitlines()
                data = sock.recv(1024)
            except socket.timeout:
                    print 'XBMC2: Search finished, results in address dropbox.'
                    break
            else:
                headers = Headers(data)
                if "HTTP/1.1 200 OK" == headers['Start-line']:
                    if headers['USN'] not in USNCache:
                        USNCache.append(headers['USN'])
                        ssdpResultList.append(headers['LOCATION'])
                        #with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'ssdp.log'), 'a') as f:
                        #    f.write(data)

    for result in ssdpResultList:
        #with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'ssdp.log'), 'a') as f:
        #    f.write(urllib2.urlopen(result).read())
        doc = xml.dom.minidom.parse(urllib2.urlopen(result))
        for modelName in doc.getElementsByTagName("modelName"):
            if modelName.firstChild.data in ('XBMC Media Center', 'Kodi'):
                XBMCResultList[urlparse(doc.getElementsByTagName("presentationURL")[0].firstChild.data).netloc] = doc.getElementsByTagName("friendlyName")[0].firstChild.data
    return XBMCResultList

def CheckDefault(Dict1, Dict2):
    for i in Dict1.iterkeys():
        if type(Dict1[i]) is dict:
            try:
                CheckDefault(Dict1[i], Dict2[i])
            except KeyError:
                Dict2[i] = Dict1[i].copy()
        else:
            if not Dict2.has_key(i):
                Dict2[i] = Dict1[i]

# And now we define the actual plugin:

class XBMC2(eg.PluginClass):
    pluginConfigDefault = {
        'XBMC': {
            'ip': '127.0.0.1',
            'port': 80,
            'username': '',
            'password': '',
        },
        'EventServer': {
            'enable': True,
            'port': 9777,
        },
        'JSONRPC': {
            'enable': False,
            'port': 9090,
            #'retrys': 5,
            #'retryTime': 5,
        },
        'Broadcast':{
                'enable': False,
                'port': 8278,
                #'workaround': False,
        },
        'logRawEvents': False,
        'logDebug': False,
    }

    def __init__(self):
        ButtonsGroup = self.AddGroup("Buttons", "Button actions to send to XBMC")
        ButtonsGroup.AddActionsFromList(REMOTE_BUTTONS, ButtonPrototype)
        ButtonsGroup.AddActionsFromList(GAMEPAD_BUTTONS, GamepadPrototype)
        ButtonsGroup.AddActionsFromList(APPLEREMOTE_BUTTONS, AppleRemotePrototype)
        ButtonsGroup.AddActionsFromList(KEYBOARD_KEYS, KeyboardPrototype)
        ActionsGroup = self.AddGroup("Actions", "Actions to send to XBMC")
        ActionsGroup.AddActionsFromList(GENERAL_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(MEDIA_PLAYING_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(PLAYLIST_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(FULLSCREEN_VIDEO_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(SLIDESHOW_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(CALIBRATION_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(FILEMANAGER_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(ON_SCREEN_KEYBOARD_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(VISUALISATION_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(SHUTDOWN_ACTIONS, ActionPrototype)
        ActionsGroup.AddActionsFromList(UNCATEGORIZED_ACTIONS, ActionPrototype)
        try:
                    MANUALLYUPDATED_ACTIONS = [[eg.ActionGroup, "ManuallyUpdated", "Manually Updated", None, readData('actions.dat')]]
        except IOError:
          #eg.PrintError('Failed to open: httpapi.dat')
          pass
        else:
          ActionsGroup.AddActionsFromList(MANUALLYUPDATED_ACTIONS, ActionPrototype)

        ConfigurableGroup = ActionsGroup.AddGroup("Configurable", "Actions that have configurable settings")
        ConfigurableGroup.AddAction(UpdateLibrary)
        self.AddActionsFromList(WINDOWS, ActionPrototype)

        TestGroup = self.AddGroup("Experimental", "Experimental")
        TestGroup.AddAction(JSONRPC)
        TestGroup.AddAction(HTTPAPI)
        TestGroup.AddAction(BuiltInFunctions)
        TestGroup.AddAction(GetCurrentlyPlayingFilename)
        TestGroup.AddAction(SendNotification)

#        self.AddAction(StopRepeating)
        self.xbmc = XBMCClient("EventGhost")
        self.JSON_RPC = XBMC_JSON_RPC()
        self.HTTP_API = XBMC_HTTP_API()
        self.stopJSONRPCNotifications = Event()
        self.stopBroadcastEvents = Event()

    def Configure(self, pluginConfig={}, *args):
                def UpdateActions(event):
                    def GetActions():
                        URL = 'https://raw.githubusercontent.com/xbmc/xbmc/Krypton/xbmc/input/ButtonTranslator.cpp'
                        request = urllib2.Request(URL)
                        #try:
                        w = urllib2.urlopen(request)
                        #except urllib2.HTTPError:
                        #    #Page = Cache[URL]['Page']
                        #    pass
                        #else:
                        Page1 = w.read().splitlines(False)
                        #print repr(Page1)

                        #print Page1.index("static const ActionMapping actions[] =")
                        #print Page1[Page1.index("static const ActionMapping actions[] ="):].index("};")
                        Page1a = Page1[Page1.index("static const ActionMapping actions[] =") + 2:Page1.index("};")]
                        #print Page1a[0]
                        #print len(Page1a)

                        ActionList = []
                        for i in Page1a:
                            try:
                                #print repr(i.split('"')[1])
                                ActionList.append(i.split('"')[1])
                            except IndexError:
                                pass
                        return ActionList

                    def GetActionDescriptions():
                        def XMLText(Node):
                            text = ''
                            #print "Info:", Node.nodeValue, Node.nodeName
                            try:
                                for n in Node.childNodes:
                                    text += n.nodeValue if n.nodeName == '#text' else XMLText(n)
                                #print "Text:", text
                            except:
                                print "Try:", Node.nodeValue, Node.nodeName

                            return text

                        UserAgent = 'XBMC2 EventGhost plugin'
                        URL = 'http://kodi.wiki/view/Action_IDs'
                        hdr = {'User-Agent': UserAgent}

                        request = urllib2.Request(URL, headers=hdr)
                        w = urllib2.urlopen(request)

                        Page2 = w.read()

                        ActionDict = {}
                        for tr in xml.dom.minidom.parseString(Page2).getElementsByTagName("tr"):
                            #for code in xml.dom.minidom.parseString(Page2).getElementsByTagName("code"):
                            for code in tr.getElementsByTagName("code")[0:1]:
                                #code = tr.getElementsByTagName("code")[0]
                                #print "code:", code.nodeValue, code.nodeName
                                #print repr(code.childNodes.item(0).nodeValue)
                                if '2-9' in XMLText(code).strip():
                                    for i in range(2, 10):
                                        #print i
                                        #print repr((XMLText(code).strip()[:-5] + str(i)).lower()),
                                        #print repr(XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                        ActionDict[(XMLText(code).strip()[:-5] + str(i)).lower()] = ((XMLText(code).strip()[:-5] + str(i)), XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                elif '0-9' in XMLText(code).strip():
                                    for i in range(10):
                                        #print i
                                        #print repr((XMLText(code).strip()[:-5] + str(i)).lower()),
                                        #print repr(XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                        ActionDict[(XMLText(code).strip()[:-5] + str(i)).lower()] = ((XMLText(code).strip()[:-5] + str(i)), XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                else:
                                    if XMLText(code).strip().lower() not in ActionDict:
                                        #print repr(XMLText(code).strip().lower()),
                                        #print repr(XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                        ActionDict[XMLText(code).strip().lower()] = (XMLText(code).strip(), XMLText(code.parentNode.nextSibling.nextSibling).strip())
                                        #print "End"
                        return ActionDict

                    ActionList = GetActions()
                    ActionDict = GetActionDescriptions()

                    for a in GENERAL_ACTIONS[0][4] + MEDIA_PLAYING_ACTIONS[0][4] + PLAYLIST_ACTIONS[0][4] + FULLSCREEN_VIDEO_ACTIONS[0][4] + SLIDESHOW_ACTIONS[0][4] + CALIBRATION_ACTIONS[0][4] + FILEMANAGER_ACTIONS[0][4] + ON_SCREEN_KEYBOARD_ACTIONS[0][4] + VISUALISATION_ACTIONS[0][4] + SHUTDOWN_ACTIONS[0][4] + UNCATEGORIZED_ACTIONS[0][4]:
                        #print a
                        if a[3].lower() in ActionList:
                            ActionList.remove(a[3])
                        #else:
                        #    print repr(a)

                    EGActionList = []
                    for action in sorted(ActionList):
                        try:
                            ActionDict[action]
                            #print repr((action, ActionDict[action][0], ActionDict[action][1], action))
                            EGActionList.append((action, ActionDict[action][0], ActionDict[action][1], action))
                        except KeyError:
                            #print "Description missing:", action
                            pass
                    #print repr(EGActionList)

                    writeData('actions.dat', EGActionList)
                    print 'XBMC2: "Manually added" actions updated, restart EventGhost to use.'
                def ConnectionTest(event):
                    print "XBMC2: Starting connection test, trying to connect to XBMC using", panel.combo_box_IP.GetValue()

                    self.JSON_RPC.connect(ip=panel.combo_box_IP.GetValue().split(':')[0], port=panel.combo_box_IP.GetValue().split(':')[1], username=panel.text_ctrl_Username.GetValue(), password=panel.text_ctrl_Password.GetValue())
                    try:
                        result = self.JSON_RPC.send('GUI.ShowNotification', ast.literal_eval("['XBMC2 for EventGhost','Connection test, JSON-RPC works.']"))
                    except urllib2.HTTPError as e:
                        eg.PrintError('XBMC2:', str(e))
                        print 'XBMC2: Please check that your username and password are correct.'
                    except urllib2.URLError as e:
                        eg.PrintError('XBMC2:', str(e.reason))
                        print 'XBMC2: Please check that XBMC is running, that your IP address and port are correct.\nXBMC2: Also in XBMCs settings\\Services\\Webserver, "Allow control of XBMC via HTTP" needs to be set.'
                    except ValueError as e:
                        eg.PrintError("XBMC2: Server responded but didn't provide valid JSON data. Check that your IP and port are correct.")
                    except:
                        eg.PrintError('XBMC2: Unknown error: ')
                        import sys, traceback
                        traceback.print_exc()
                    finally:
                        self.JSON_RPC.close()

                    try:
                        if result['result'] == 'OK':
                            print 'XBMC2: JSON-RPC works.'
                    #except KeyError:
                    #    eg.PrintError('XBMC2: JSON-RPC error: ', str(result['error']['message']))
                    except:
                        self.HTTP_API.connect(ip=panel.combo_box_IP.GetValue().split(':')[0], port=panel.combo_box_IP.GetValue().split(':')[1], username=panel.text_ctrl_Username.GetValue(), password=panel.text_ctrl_Password.GetValue())
                        try:
                            result = self.HTTP_API.send('ExecBuiltIn', 'Notification(XBMC2 for EventGhost, Connection test. HTTPAPI works.)')
                        except urllib2.HTTPError as e:
                            if e.code == 401:
                                eg.PrintError('XBMC2:', str(e))
                                print 'XBMC2: Please check that your username and password are correct.'
                            else:
                                eg.PrintError('XBMC2:', str(e))
                                print 'XBMC2: Please check that XBMC is running, that your IP address and port are correct.\nXBMC2: Also in XBMCs settings\\Network\\Services\\ "Allow control of XBMC via HTTP" needs to be set.'
                        except urllib2.URLError as e:
                            eg.PrintError('XBMC2:', str(e.reason))
                            print 'XBMC2: Please check that XBMC is running, that your IP address and port are correct.\nXBMC2: Also in XBMCs settings\\Network\\Services\\ "Allow control of XBMC via HTTP" needs to be set.'
                        except:
                            eg.PrintError('XBMC2: Unknown error: ')
                            import sys, traceback
                            traceback.print_exc()
                        else:
                                if result == 'OK':
                                    print 'XBMC2: HTTPAPI works.', result
                                else:
                                    eg.PrintError('XBMC2: HTTPAPI error: ', result)
                                    self.xbmc.connect(ip=panel.combo_box_IP.GetValue().split(':')[0])
                                    self.xbmc.send_notification('XBMC2 for EventGhost', 'Connection test, if you see this your IP address is correct.')
                                    self.xbmc.close()
                        finally:
                            self.HTTP_API.close()

                def SearchForXBMC(event):
                    for i in ssdpSearch().keys():
                        panel.combo_box_IP.Append(i)

                def initPanel(self):
                    self.combo_box_IP = wx.ComboBox(self, wx.ID_ANY, value=pluginConfig['XBMC']['ip']+':'+str(pluginConfig['XBMC']['port']), choices=["127.0.0.1:80"], style=wx.CB_DROPDOWN)
                    self.button_IPTest = wx.Button(self, wx.ID_ANY, "Test")

                    self.button_UpdateActions = wx.Button(self, wx.ID_ANY, "Update Actions")

                    self.button_Search = wx.Button(self, wx.ID_ANY, "Search")
                    self.label_Username = wx.StaticText(self, wx.ID_ANY, "Username")
                    self.text_ctrl_Username = wx.TextCtrl(self, wx.ID_ANY, pluginConfig['XBMC']['username'])
                    self.label_Password = wx.StaticText(self, wx.ID_ANY, "Password")
                    self.text_ctrl_Password = wx.TextCtrl(self, wx.ID_ANY, pluginConfig['XBMC']['password'], style=wx.TE_PASSWORD)
                    self.sizer_Global_staticbox = wx.StaticBox(self, wx.ID_ANY, "IP address and port of XBMC (127.0.01 is this computer)")
                    self.checkbox_EventServerEnable = wx.CheckBox(self, wx.ID_ANY, "Enable")
                    self.label_EventServerPort = wx.StaticText(self, wx.ID_ANY, "Port")
                    self.spin_ctrl_EventServerPort = wx.SpinCtrl(self, wx.ID_ANY, str(pluginConfig['EventServer']['port']), min=0, max=65535)
                    self.sizer_EventServer_staticbox = wx.StaticBox(self, wx.ID_ANY, "EventServer")
                    self.checkbox_JSONRPCEnable = wx.CheckBox(self, wx.ID_ANY, "Enable")
                    self.label_Port = wx.StaticText(self, wx.ID_ANY, "Port")
                    self.spin_ctrl_JSONRPCPort = wx.SpinCtrl(self, wx.ID_ANY, str(pluginConfig['JSONRPC']['port']), min=0, max=65535)
                    self.label_Retrys = wx.StaticText(self, wx.ID_ANY, "Retrys")
                    self.spin_ctrl_Retrys = wx.SpinCtrl(self, wx.ID_ANY, "5", min=0, max=100)
                    self.label_Time = wx.StaticText(self, wx.ID_ANY, "Time between retrys")
                    self.spin_ctrl_Time = wx.SpinCtrl(self, wx.ID_ANY, "5", min=0, max=100)
                    self.label_Seconds = wx.StaticText(self, wx.ID_ANY, "Seconds")
                    self.sizer_JSONRPC_staticbox = wx.StaticBox(self, wx.ID_ANY, "JSON-RPC notifications")
                    self.checkbox_BroadcastEnable = wx.CheckBox(self, wx.ID_ANY, "Enable")
                    self.label_BroadcastPort = wx.StaticText(self, wx.ID_ANY, "Port")
                    self.spin_ctrl_BroadcastPort = wx.SpinCtrl(self, wx.ID_ANY, str(pluginConfig['Broadcast']['port']), min=0, max=65535)
                    self.checkbox_BroadcastWorkaround = wx.CheckBox(self, wx.ID_ANY, "Repeating events workaround")
                    self.sizer_Broadcast_staticbox = wx.StaticBox(self, wx.ID_ANY, "Broadcast events")
                    self.checkbox_logRawEvents = wx.CheckBox(self, wx.ID_ANY, "Log raw events")

                    self.checkbox_logDebug = wx.CheckBox(self, wx.ID_ANY, "Debug")

                    self.sizer_Events_staticbox = wx.StaticBox(self, wx.ID_ANY, "Event settings")
                    self.button_IPTest.Bind(wx.EVT_BUTTON, ConnectionTest)

                    self.button_UpdateActions.Bind(wx.EVT_BUTTON, UpdateActions)

                    self.button_Search.Bind(wx.EVT_BUTTON, SearchForXBMC)
                    setPanelProperties(self)
                    doPanelLayout(self)
                def setPanelProperties(self):
                    self.combo_box_IP.SetMinSize((147, 21))
                    self.combo_box_IP.SetToolTipString("IP address of the XBMC you want to control.")
                    self.button_IPTest.SetToolTipString("Test to connect to XBMC")

                    self.button_UpdateActions.SetToolTipString('Add any new XBMC actions to a category "Manually Updated". You need to restart EventGhost for the new actions to be visible')

                    self.button_Search.SetToolTipString("Search for any XBMCs that are running and reachable over the LAN.")
                    self.text_ctrl_Username.SetToolTipString("Username that are specified in XBMC")
                    self.text_ctrl_Password.SetToolTipString("Password that are specified in XBMC")
                    self.checkbox_EventServerEnable.Enable(False)
                    self.checkbox_EventServerEnable.SetValue(1)
                    self.spin_ctrl_EventServerPort.SetMinSize((60, -1))
                    self.spin_ctrl_EventServerPort.SetToolTipString("Port used by XBMC to recieve notifications")
                    self.checkbox_JSONRPCEnable.SetToolTipString("Enable JSON-RPC notifications")
                    self.checkbox_JSONRPCEnable.SetValue(pluginConfig['JSONRPC']['enable'])
                    self.checkbox_BroadcastEnable.SetValue(pluginConfig['Broadcast']['enable'])
                    self.checkbox_logRawEvents.SetValue(pluginConfig['logRawEvents'])

                    self.checkbox_logDebug.SetValue(pluginConfig['logDebug'])

                    self.spin_ctrl_JSONRPCPort.SetMinSize((60, -1))
                    self.spin_ctrl_JSONRPCPort.SetToolTipString("Port used by XBMC to recieve notifications")
                    self.spin_ctrl_Retrys.SetMinSize((50, -1))
                    self.spin_ctrl_Time.SetMinSize((50, -1))
                    self.checkbox_logRawEvents.SetToolTipString("Show any events from XBMC in the log, exactly as XBMC sends them.")

                    self.checkbox_logDebug.SetToolTipString("Activate debugging messages in the log(Will spam the log so use only when needed).")
                def doPanelLayout(self):
                    self.sizer = wx.BoxSizer(wx.VERTICAL)
                    self.sizer_Events_staticbox.Lower()
                    sizer_Events = wx.StaticBoxSizer(self.sizer_Events_staticbox, wx.VERTICAL)
                    self.sizer_Broadcast_staticbox.Lower()
                    sizer_Broadcast = wx.StaticBoxSizer(self.sizer_Broadcast_staticbox, wx.HORIZONTAL)
                    sizer_BroadcastEnable = wx.BoxSizer(wx.VERTICAL)
                    sizer_BroadcastPort = wx.BoxSizer(wx.HORIZONTAL)
                    self.sizer_JSONRPC_staticbox.Lower()
                    sizer_JSONRPC = wx.StaticBoxSizer(self.sizer_JSONRPC_staticbox, wx.VERTICAL)
                    sizer_14 = wx.BoxSizer(wx.HORIZONTAL)
                    sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
                    self.sizer_EventServer_staticbox.Lower()
                    sizer_EventServer = wx.StaticBoxSizer(self.sizer_EventServer_staticbox, wx.VERTICAL)
                    sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
                    self.sizer_Global_staticbox.Lower()
                    sizer_Global = wx.StaticBoxSizer(self.sizer_Global_staticbox, wx.VERTICAL)
                    sizer_Password = wx.BoxSizer(wx.HORIZONTAL)
                    sizer_IPPort = wx.BoxSizer(wx.HORIZONTAL)
                    sizer_IPPort.Add(self.combo_box_IP, 0, 0, 0)
                    sizer_IPPort.Add(self.button_IPTest, 0, 0, 0)
                    sizer_IPPort.Add(self.button_Search, 0, 0, 0)
                    sizer_Global.Add(sizer_IPPort, 1, wx.EXPAND, 0)
                    sizer_Password.Add(self.label_Username, 0, 0, 0)
                    sizer_Password.Add(self.text_ctrl_Username, 0, 0, 0)
                    sizer_Password.Add(self.label_Password, 0, 0, 0)
                    sizer_Password.Add(self.text_ctrl_Password, 0, 0, 0)
                    sizer_Global.Add(sizer_Password, 1, wx.EXPAND, 0)
                    sizer_2.Add(sizer_Global, 0, 0, 0)
                    sizer_EventServer.Add(self.checkbox_EventServerEnable, 0, 0, 0)
                    sizer_1.Add(self.label_EventServerPort, 0, 0, 0)
                    sizer_1.Add(self.spin_ctrl_EventServerPort, 0, 0, 0)
                    sizer_EventServer.Add(sizer_1, 1, wx.SHAPED, 0)
                    sizer_2.Add(sizer_EventServer, 0, wx.SHAPED, 0)
                    self.sizer.Add(sizer_2, 0, wx.EXPAND, 0)
                    sizer_JSONRPC.Add(self.checkbox_JSONRPCEnable, 0, 0, 0)
                    sizer_14.Add(self.label_Port, 0, 0, 0)
                    sizer_14.Add(self.spin_ctrl_JSONRPCPort, 0, 0, 0)
                    sizer_14.Add(self.label_Retrys, 0, 0, 0)
                    sizer_14.Add(self.spin_ctrl_Retrys, 0, 0, 0)
                    sizer_14.Add(self.label_Time, 0, 0, 0)
                    sizer_14.Add(self.spin_ctrl_Time, 0, 0, 0)
                    sizer_14.Add(self.label_Seconds, 0, 0, 0)
                    sizer_JSONRPC.Add(sizer_14, 1, wx.EXPAND, 0)
                    sizer_Events.Add(sizer_JSONRPC, 1, wx.EXPAND, 0)
                    sizer_BroadcastEnable.Add(self.checkbox_BroadcastEnable, 0, 0, 0)
                    sizer_BroadcastPort.Add(self.label_BroadcastPort, 0, 0, 0)
                    sizer_BroadcastPort.Add(self.spin_ctrl_BroadcastPort, 0, 0, 0)
                    sizer_BroadcastPort.Add(self.checkbox_BroadcastWorkaround, 0, 0, 0)
                    sizer_BroadcastEnable.Add(sizer_BroadcastPort, 1, wx.EXPAND, 0)
                    sizer_Broadcast.Add(sizer_BroadcastEnable, 1, wx.EXPAND, 0)
                    sizer_Events.Add(sizer_Broadcast, 1, wx.EXPAND, 0)
                    sizer_Events.Add(self.checkbox_logRawEvents, 0, 0, 0)

                    sizer_Events.Add(self.checkbox_logDebug, 0, 0, 0)

                    sizer_Events.Add(self.button_UpdateActions, 0, 0, 0)

                    self.sizer.Add(sizer_Events, 1, wx.EXPAND, 0)
                    self.sizer.Fit(self)

                if type(pluginConfig) is not dict:
                    pluginConfig = {}
                CheckDefault(self.pluginConfigDefault, pluginConfig)

                panel = eg.ConfigPanel()
                initPanel(panel)
#        textControl = panel.ComboBox(
#            ip,
#            IPs,
#            style=wx.CB_DROPDOWN,
#            validator=eg.DigitOnlyValidator()
#        )
                while panel.Affirmed():
                    changed = False
                    if pluginConfig['XBMC']['ip'] != panel.combo_box_IP.GetValue().split(':')[0]:
                        pluginConfig['XBMC']['ip'] = panel.combo_box_IP.GetValue().split(':')[0]
                        changed = True
                    if pluginConfig['XBMC']['port'] != int(panel.combo_box_IP.GetValue().split(':')[1]):
                        pluginConfig['XBMC']['port'] = int(panel.combo_box_IP.GetValue().split(':')[1])
                        changed = True
                    if pluginConfig['XBMC']['username'] != panel.text_ctrl_Username.GetValue():
                        pluginConfig['XBMC']['username'] = panel.text_ctrl_Username.GetValue()
                        changed = True
                    if pluginConfig['XBMC']['password'] != panel.text_ctrl_Password.GetValue():
                        pluginConfig['XBMC']['password'] = panel.text_ctrl_Password.GetValue()
                        changed = True
                    if pluginConfig['EventServer']['port'] != int(panel.spin_ctrl_EventServerPort.GetValue()):
                        pluginConfig['EventServer']['port'] = int(panel.spin_ctrl_EventServerPort.GetValue())
                        changed = True
                    if pluginConfig['JSONRPC']['enable'] != panel.checkbox_JSONRPCEnable.GetValue():
                        pluginConfig['JSONRPC']['enable'] = panel.checkbox_JSONRPCEnable.GetValue()
                        changed = True
                    if pluginConfig['JSONRPC']['port'] != int(panel.spin_ctrl_JSONRPCPort.GetValue()):
                        pluginConfig['JSONRPC']['port'] = int(panel.spin_ctrl_JSONRPCPort.GetValue())
                        changed = True
                    if pluginConfig['Broadcast']['enable'] != panel.checkbox_BroadcastEnable.GetValue():
                        pluginConfig['Broadcast']['enable'] = panel.checkbox_BroadcastEnable.GetValue()
                        changed = True
                    if pluginConfig['Broadcast']['port'] != int(panel.spin_ctrl_BroadcastPort.GetValue()):
                        pluginConfig['Broadcast']['port'] = int(panel.spin_ctrl_BroadcastPort.GetValue())
                        changed = True
                    if pluginConfig['logRawEvents'] != panel.checkbox_logRawEvents.GetValue():
                        pluginConfig['logRawEvents'] = panel.checkbox_logRawEvents.GetValue()
                        changed = True
                    if pluginConfig['logDebug'] != panel.checkbox_logDebug.GetValue():
                        pluginConfig['logDebug'] = panel.checkbox_logDebug.GetValue()
                        changed = True
                    #pluginConfig['JSONRPC']['retrys'] = int(JSONRPCNotificationRetrys.GetValue())
                    #pluginConfig['JSONRPC']['retryTime'] = int(JSONRPCNotificationRetryTime.GetValue())
                    try:
                        panel.SetResult(pluginConfig, (args[0], not(args[0]))[changed])
                    except:
                        panel.SetResult(pluginConfig, changed)

    def __start__(self, pluginConfig={}, *args):
                if type(pluginConfig) is not dict:
                    pluginConfig = {}
                CheckDefault(self.pluginConfigDefault, pluginConfig)

                self.pluginConfig = pluginConfig
                try:
                        self.xbmc.connect(ip=pluginConfig['XBMC']['ip'], port=pluginConfig['EventServer']['port'])
                except:
                        raise self.Exceptions.ProgramNotRunning
                self.JSON_RPC.connect(ip=pluginConfig['XBMC']['ip'], port=pluginConfig['XBMC']['port'], username=pluginConfig['XBMC']['username'], password=pluginConfig['XBMC']['password'])
                self.HTTP_API.connect(ip=pluginConfig['XBMC']['ip'], port=pluginConfig['XBMC']['port'], username=pluginConfig['XBMC']['username'], password=pluginConfig['XBMC']['password'])
                if self.pluginConfig['JSONRPC']['enable']:
                    try:
                        self.JSONRPCNotificationsThread.join(10)
                    except:
                        self.stopJSONRPCNotifications.clear()
                        self.JSONRPCNotificationsThread = Thread(target=self.JSONRPCNotifications, args=(self.stopJSONRPCNotifications,))
                        self.JSONRPCNotificationsThread.start()
                    else:
                        if not self.JSONRPCNotificationsThread.isAlive():
                            self.stopJSONRPCNotifications.clear()
                            self.JSONRPCNotificationsThread = Thread(target=self.JSONRPCNotifications, args=(self.stopJSONRPCNotifications,))
                            self.JSONRPCNotificationsThread.start()
                        else:
                            print "XBMC2: Can't stop old JSON-RPC notification thread, will not start a new one."

                if self.pluginConfig['Broadcast']['enable']:
                    self.stopBroadcastEvents.clear()
                    BroadcastEventsThread = Thread(target=self.BroadcastEvents, args=(self.stopBroadcastEvents,))
                    BroadcastEventsThread.start()

    def __stop__(self):
                #if self.pluginConfig['JSONRPC']['enable']:
                self.stopJSONRPCNotifications.set()
                #if self.pluginConfig['Broadcast']['enable']:
                self.stopBroadcastEvents.set()
                try:
                        self.xbmc.close()
                except:
                        pass

    def __close__(self):
        pass

#    def ThreadWorker(self, stopThreadEvent):
#        while not stopThreadEvent.isSet():
#            self.TriggerEvent("MyTimerEvent")
#            stopThreadEvent.wait(10.0)

    def JSONRPCNotifications(self, stopJSONRPCNotifications):
            import os
            import struct
            from collections import deque
            import select
            debug = self.pluginConfig['logDebug']
            def SSDPInit(SSDP_IP, SSDP_PORT):
                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                sock.settimeout(10)
                sock.bind(('', SSDP_PORT))
                mreq = struct.pack("4sl", socket.inet_aton(SSDP_IP), socket.INADDR_ANY)
                sock.setsockopt(socket.IPPROTO_IP, socket.SO_DEBUG, True)
                try:
                    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                except:
                    if debug:
                        eg.PrintError('JSON-RPC connect error: ')
                        import sys, traceback
                        traceback.print_exc()
                    try:
                        INTERFACE_ADDR = socket.gethostbyname(socket.gethostname())
                        mreq = socket.inet_aton(SSDP_IP) + socket.inet_aton(INTERFACE_ADDR)
                        sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
                    except:
                        eg.PrintError('JSON-RPC connect error: ')
                        import sys, traceback
                        traceback.print_exc()
                        raise
                #sock.settimeout(10)
                #sock.bind(('', SSDP_PORT))
                return sock
            def Headers(data):
                headers = {}
                for line in data.splitlines():
                    if not line.split(':', 1)[0]:
                        continue
                    try:
                        headers[line.split(':', 1)[0].upper()] = line.split(':', 1)[1]
                    except:
                        headers['Start-line'] = line.split(':', 1)[0]
                return headers
            def interface_addresses(family=socket.AF_INET):
                for fam, _, _, _, sockaddr in socket.getaddrinfo('', None):
                    if family == fam:
                        yield sockaddr[0]

            def JSONSplit(data):
                parts = []
                rest = data
                while rest:
                    part = ''
                    while True:
                        try:
                            part += rest[:rest.index('}')+1]
                        except ValueError:
                            rest = ''
                            break
                        else:
                            rest = rest[rest.find('}')+1:]
                            try:
                                parts.append(json.loads(part))
                                break
                            except:
                                continue
                return parts
            def BufferedRead(Socket):
                _PacketSize = 4096
                Buffer = deque()
                rlist = [Socket, ]
                while not stopJSONRPCNotifications.isSet():
                    if Buffer:
                        ready, _, _ = select.select(rlist, [], [], 0)
                        if ready:
                            data = Socket.recv(_PacketSize)
                        else:
                            #print('{0}: Buffer: {1}\n'.format(MyName, len(Buffer)), end='')
                            #Q.put((MyName, Buffer.popleft(), len(Buffer)), True)
                            yield Buffer.popleft()
                            continue
                    else:
                        try:
                            data = Socket.recv(_PacketSize)
                        except socket.timeout:
                            #logging.debug('SSDPListener: Wait for event: Timeout.')
                            if debug:
                                print 'XBMC2: SSDP: Wait for event: Timeout.'
                            continue
                    Buffer.append(data)
                if debug:
                    print 'XBMC2: SSDP: Wait for event: Stop recieving messages.'

            def WaitForXBMC(Socket):
                USNCache = []
                XBMCDetected = False
                BRead = BufferedRead(Socket)

                if debug:
                    print 'XBMC2: SSDP is on'
                while not (stopJSONRPCNotifications.isSet() or XBMCDetected):
                    if debug:
                        print 'XBMC2: SSDP: Wait for event.'
                    try:
                        #data = sock.recv(4096)
                        data = BRead.next()
                        headers = Headers(data)
                        #headers = Headers(sock.recv(4096))
                    except socket.timeout:
                        if debug:
                            print 'XBMC2: SSDP: Wait for event: Timeout.'
                        pass
                    except StopIteration:
                        continue
                    else:
                        try:
                            if debug:
                                with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'ssdp.log'), 'a+') as f:
                                    f.write("Got ssdp message.")
                            if "NOTIFY * HTTP/1.1" == headers['Start-line']:
                                try:
                                    if headers['USN'].split(':', 2)[1] not in USNCache:
                                        try:
                                            doc = xml.dom.minidom.parse(urllib2.urlopen(headers['LOCATION']))
                                        except:
                                            continue
                                        else:
                                            for modelName in doc.getElementsByTagName("modelName"):
                                                if modelName.firstChild.data in ('Kodi', 'XBMC Media Center', 'XBMC'):
                                                    if debug:
                                                        with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'ssdp.log'), 'a+') as f:
                                                            f.write(data)
                                                            f.write(urllib2.urlopen(headers['LOCATION']).read())
                                                        print 'XBMC2: SSDP modelName:', modelName.firstChild.data
                                                    #from urlparse import urlparse
                                                    if self.pluginConfig['XBMC']['ip'] == '127.0.0.1':
                                                        for ip in interface_addresses():
                                                            #if urlparse(doc.getElementsByTagName("presentationURL")[0].firstChild.data).netloc == ip+':'+str(self.pluginConfig['XBMC']['port']):
                                                            if urlparse(doc.getElementsByTagName("presentationURL")[0].firstChild.data).netloc.split(":")[0] == ip:
                                                                try:
                                                                    if urlparse(doc.getElementsByTagName("presentationURL")[0].firstChild.data).netloc.split(":")[1] == str(self.pluginConfig['XBMC']['port']):
                                                                        XBMCDetected = True
                                                                        break
                                                                    else:
                                                                        continue
                                                                except:
                                                                    pass
                                                                XBMCDetected = True
                                                                break
                                                    else:
                                                        if debug:
                                                            print 'XBMC2: SSDP address:', urlparse(doc.getElementsByTagName("presentationURL")[0].firstChild.data).netloc
                                                        if urlparse(doc.getElementsByTagName("presentationURL")[0].firstChild.data).netloc == self.pluginConfig['XBMC']['ip']+':'+str(self.pluginConfig['XBMC']['port']):
                                                            XBMCDetected = True
                                                else:
                                                    if debug:
                                                        with open(os.path.join(eg.folderPath.RoamingAppData, 'EventGhost', 'plugins', 'XBMC2', 'ssdp.log'), 'a+') as f:
                                                            f.write(data)
                                                            f.write(urllib2.urlopen(headers['LOCATION']).read())
                                                        print 'XBMC2: SSDP unknown modelName:', modelName.firstChild.data
                                            USNCache.append(headers['USN'].split(':', 2)[1])
                                except IndexError:
                                    if debug:
                                        print 'XBMC2: SSDP: No USN in headers:', headers
                                    continue
                            elif "M-SEARCH * HTTP/1.1" == headers['Start-line']:
                                if 'Kodi' in headers['USER-AGENT']:
                                    if debug:
                                        print 'XBMC2: SSDP: Found search message from Kodi:', headers['USER-AGENT']
                                    XBMCDetected = True
                                    break
                        except KeyError:
                            if debug:
                                print 'XBMC2: SSDP: "Start-line" test failed: Content of headers:', headers
                                eg.PrintError('JSON-RPC connect error: ')
                                import sys, traceback
                                traceback.print_exc()
                        except:
                            if debug:
                                print 'XBMC2: SSDP: Error in headers:', headers
                                eg.PrintError('JSON-RPC connect error: ')
                                import sys, traceback
                                traceback.print_exc()

                #sock.close()
                if debug:
                    print 'XBMC2: SSDP is off'

            SSDP_IP = '239.255.255.250'
            SSDP_PORT = 1900
            Socket = SSDPInit(SSDP_IP, SSDP_PORT)
            print "XBMC2: Activating JSON-RPC notifications"
            while not stopJSONRPCNotifications.isSet():
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(10)
                try:
                    if debug:
                        print "XBMC2: Connecting to XBMC, to be able to recive JSON-RPC notifications."
                    s.connect((self.pluginConfig['XBMC']['ip'], self.pluginConfig['JSONRPC']['port']))
                except socket.error:
                    if debug:
                        eg.PrintError('XBMC2: connection error: ')
                        import sys, traceback
                        traceback.print_exc()
                        print "XBMC2: Not able to connect to XBMC, will use SSDP to detect when XBMC is available."
                    WaitForXBMC(Socket)
                else:
                    print "XBMC2: Connected to XBMC (", self.pluginConfig['XBMC']['ip'], ":", self.pluginConfig['JSONRPC']['port'], "), ready to recive JSON-RPC notifications."
                    self.TriggerEvent('System.OnStart')
                    message = ''
                    if debug:
                        print 'XBMC2: JSON-RPC sent: "GetConfiguration".'
                        s.send(json.dumps({'jsonrpc':'2.0', 'method': 'JSONRPC.GetConfiguration', 'id':1}))
                    while not stopJSONRPCNotifications.isSet():
                        try:
                            if debug:
                                print "XBMC2: JSON-RPC notifications: Wait for event."
                            message += s.recv(4096)
                            #print len(message)
                            if not message:
                                break
                        except socket.timeout:
                            if debug:
                                print "XBMC2: JSON-RPC notifications: Wait for event: Timeout."
                            s.send(json.dumps({'jsonrpc':'2.0', 'method': 'JSONRPC.Ping', 'id':1}))
                            if debug:
                                print 'XBMC2: JSON-RPC sent: "ping".'
                            continue
                        except socket.error:
                            eg.PrintError('XBMC2: JSON socket.error: ')
                            import sys, traceback
                            traceback.print_exc()
                            break
                        except:
                            eg.PrintError('XBMC2: Error: JSON-RPC event ')
                            import sys, traceback
                            traceback.print_exc()
                            break
                        else:
                            if self.pluginConfig['logRawEvents']:
                                print "XBMC2: Raw event: %s" % repr(message)
                            try:
                                messages = [json.loads(message)]
                            except:
                                #eg.PrintError('XBMC2: Error: JSON-RPC event ')
                                #import sys, traceback
                                #traceback.print_exc()
                                #eg.PrintError('XBMC2: Error decoding: JSON-RPC event \n' + "Raw event: %s" % repr(message))
                                #continue
                                messages = JSONSplit(message)
                            #else:Raw event: {u'jsonrpc': u'2.0', u'id': 1, u'result': u'pong'}
                            for message in messages:
                                if self.pluginConfig['logRawEvents']:
                                    print "Raw event: %s" % repr(message)
                                try:
                                    event = message['method']
                                except:
                                    try:
                                        message['id']
                                    except:
                                        eg.PrintError('XBMC2: Error: JSON-RPC event, "method" missing ' + repr(message))
                                        self.PrintError('JSON unrecogniced event type: \n' + "Raw event: %s" % repr(message))
                                    else:
                                        try:
                                            if 'notifications' in message['result']:
                                                if debug:
                                                    print 'XBMC2: JSON-RPC responce: "notifications":', message['result']['notifications']
                                                continue
                                        except KeyError:
                                            if debug:
                                                eg.PrintError('XBMC2: Error: JSON-RPC responce, "result" missing ' + repr(message))
                                        else:
                                            if message['result'] == 'pong':
                                                if debug:
                                                    print 'XBMC2: JSON-RPC responce: "pong".'
                                                continue
                                            else:
                                                eg.PrintError('XBMC2: Error: JSON-RPC responce, "pong" missing ' + repr(message))
                                                self.PrintError('JSON unrecogniced responce type: \n' + "Raw responce: %s" % repr(message))
                                else:
                                    try:
                                        payload = message['params']['data']
                                    except KeyError:
                                        pass
                                    else:
                                        if not payload==None:
                                            try:
                                                event += '.' + payload['item']['type']
                                                del payload['item']['type']
                                                if not payload['item']:
                                                    del payload['item']
                                            except KeyError:
                                                try:
                                                    event += '.' + payload['type']
                                                    del payload['type']
                                                except KeyError:
                                                    #self.PrintError('JSON unrecogniced event type: \n' + "Raw event: %s" % repr(message))
                                                    pass
                                            except TypeError:
                                                #self.PrintError('XBMC2: JSON unrecogniced event type: \n' + "Raw event: %s" % repr(message))
                                                pass
                                            except:
                                                self.PrintError('XBMC2: JSON unrecogniced event type: \n' + "Raw event: %s" % repr(message))

                                    if not stopJSONRPCNotifications.isSet():
                                        self.TriggerEvent(event, payload)
                        message = ''
                    s.close()
                    print "XBMC2: Disconnected from XBMC, not receiving JSON-RPC notifications."
            print "XBMC2: Deactivating JSON-RPC notifications"

    def BroadcastEvents(self, stopBroadcastEvents):
            ActionList = {
# actions that we have defined...
'0':'ACTION_NONE',
'1':'ACTION_MOVE_LEFT',
'2':'ACTION_MOVE_RIGHT',
'3':'ACTION_MOVE_UP',
'4':'ACTION_MOVE_DOWN',
'5':'ACTION_PAGE_UP',
'6':'ACTION_PAGE_DOWN',
'7':'ACTION_SELECT_ITEM',
'8':'ACTION_HIGHLIGHT_ITEM',
'9':'ACTION_PARENT_DIR',
'10':'ACTION_PREVIOUS_MENU',
'11':'ACTION_SHOW_INFO',

'12':'ACTION_PAUSE',
'13':'ACTION_STOP',
'14':'ACTION_NEXT_ITEM',
'15':'ACTION_PREV_ITEM',
'16':'ACTION_FORWARD', # Can be used to specify specific action in a window, Playback control is handled in ACTION_PLAYER_*
'17':'ACTION_REWIND', # Can be used to specify specific action in a window, Playback control is handled in ACTION_PLAYER_*

'18':'ACTION_SHOW_GUI', # toggle between GUI and movie or GUI and visualisation.
'19':'ACTION_ASPECT_RATIO', # toggle quick-access zoom modes. Can b used in videoFullScreen.zml window id=2005
'20':'ACTION_STEP_FORWARD', # seek +1% in the movie. Can b used in videoFullScreen.xml window id=2005
'21':'ACTION_STEP_BACK', # seek -1% in the movie. Can b used in videoFullScreen.xml window id=2005
'22':'ACTION_BIG_STEP_FORWARD', # seek +10% in the movie. Can b used in videoFullScreen.xml window id=2005
'23':'ACTION_BIG_STEP_BACK', # seek -10% in the movie. Can b used in videoFullScreen.xml window id=2005
'24':'ACTION_SHOW_OSD', # show/hide OSD. Can b used in videoFullScreen.xml window id=2005
'25':'ACTION_SHOW_SUBTITLES', # turn subtitles on/off. Can b used in videoFullScreen.xml window id=2005
'26':'ACTION_NEXT_SUBTITLE', # switch to next subtitle of movie. Can b used in videoFullScreen.xml window id=2005
'27':'ACTION_SHOW_CODEC', # show information about file. Can b used in videoFullScreen.xml window id=2005 and in slideshow.xml window id=2007
'28':'ACTION_NEXT_PICTURE', # show next picture of slideshow. Can b used in slideshow.xml window id=2007
'29':'ACTION_PREV_PICTURE', # show previous picture of slideshow. Can b used in slideshow.xml window id=2007
'30':'ACTION_ZOOM_OUT', # zoom in picture during slideshow. Can b used in slideshow.xml window id=2007
'31':'ACTION_ZOOM_IN', # zoom out picture during slideshow. Can b used in slideshow.xml window id=2007
'32':'ACTION_TOGGLE_SOURCE_DEST', # used to toggle between source view and destination view. Can be used in myfiles.xml window id=3
'33':'ACTION_SHOW_PLAYLIST', # used to toggle between current view and playlist view. Can b used in all mymusic xml files
'34':'ACTION_QUEUE_ITEM', # used to queue a item to the playlist. Can b used in all mymusic xml files
'35':'ACTION_REMOVE_ITEM', # not used anymore
'36':'ACTION_SHOW_FULLSCREEN', # not used anymore
'37':'ACTION_ZOOM_LEVEL_NORMAL', # zoom 1x picture during slideshow. Can b used in slideshow.xml window id=2007
'38':'ACTION_ZOOM_LEVEL_1', # zoom 2x picture during slideshow. Can b used in slideshow.xml window id=2007
'39':'ACTION_ZOOM_LEVEL_2', # zoom 3x picture during slideshow. Can b used in slideshow.xml window id=2007
'40':'ACTION_ZOOM_LEVEL_3', # zoom 4x picture during slideshow. Can b used in slideshow.xml window id=2007
'41':'ACTION_ZOOM_LEVEL_4', # zoom 5x picture during slideshow. Can b used in slideshow.xml window id=2007
'42':'ACTION_ZOOM_LEVEL_5', # zoom 6x picture during slideshow. Can b used in slideshow.xml window id=2007
'43':'ACTION_ZOOM_LEVEL_6', # zoom 7x picture during slideshow. Can b used in slideshow.xml window id=2007
'44':'ACTION_ZOOM_LEVEL_7', # zoom 8x picture during slideshow. Can b used in slideshow.xml window id=2007
'45':'ACTION_ZOOM_LEVEL_8', # zoom 9x picture during slideshow. Can b used in slideshow.xml window id=2007
'46':'ACTION_ZOOM_LEVEL_9', # zoom 10x picture during slideshow. Can b used in slideshow.xml window id=2007

'47':'ACTION_CALIBRATE_SWAP_ARROWS', # select next arrow. Can b used in: settingsScreenCalibration.xml windowid=11
'48':'ACTION_CALIBRATE_RESET', # reset calibration to defaults. Can b used in: settingsScreenCalibration.xml windowid=11/settingsUICalibration.xml windowid=10
'49':'ACTION_ANALOG_MOVE', # analog thumbstick move. Can b used in: slideshow.xml window id=2007/settingsScreenCalibration.xml windowid=11/settingsUICalibration.xml windowid=10
'50':'ACTION_ROTATE_PICTURE', # rotate current picture during slideshow. Can b used in slideshow.xml window id=2007

'52':'ACTION_SUBTITLE_DELAY_MIN', # Decrease subtitle/movie Delay. Can b used in videoFullScreen.xml window id=2005
'53':'ACTION_SUBTITLE_DELAY_PLUS', # Increase subtitle/movie Delay. Can b used in videoFullScreen.xml window id=2005
'54':'ACTION_AUDIO_DELAY_MIN', # Increase avsync delay. Can b used in videoFullScreen.xml window id=2005
'55':'ACTION_AUDIO_DELAY_PLUS', # Decrease avsync delay. Can b used in videoFullScreen.xml window id=2005
'56':'ACTION_AUDIO_NEXT_LANGUAGE', # Select next language in movie. Can b used in videoFullScreen.xml window id=2005
'57':'ACTION_CHANGE_RESOLUTION', # switch 2 next resolution. Can b used during screen calibration settingsScreenCalibration.xml windowid=11

'58':'REMOTE_0', # remote keys 0-9. are used by multiple windows
'59':'REMOTE_1', # for example in videoFullScreen.xml window id=2005 you can
'60':'REMOTE_2', # enter time (mmss) to jump to particular point in the movie
'61':'REMOTE_3',
'62':'REMOTE_4', # with spincontrols you can enter 3digit number to quickly set
'63':'REMOTE_5', # spincontrol to desired value
'64':'REMOTE_6',
'65':'REMOTE_7',
'66':'REMOTE_8',
'67':'REMOTE_9',

'68':'ACTION_PLAY', # Unused at the moment
'69':'ACTION_OSD_SHOW_LEFT', # Move left in OSD. Can b used in videoFullScreen.xml window id=2005
'70':'ACTION_OSD_SHOW_RIGHT', # Move right in OSD. Can b used in videoFullScreen.xml window id=2005
'71':'ACTION_OSD_SHOW_UP', # Move up in OSD. Can b used in videoFullScreen.xml window id=2005
'72':'ACTION_OSD_SHOW_DOWN', # Move down in OSD. Can b used in videoFullScreen.xml window id=2005
'73':'ACTION_OSD_SHOW_SELECT', # toggle/select option in OSD. Can b used in videoFullScreen.xml window id=2005
'74':'ACTION_OSD_SHOW_VALUE_PLUS', # increase value of current option in OSD. Can b used in videoFullScreen.xml window id=2005
'75':'ACTION_OSD_SHOW_VALUE_MIN', # decrease value of current option in OSD. Can b used in videoFullScreen.xml window id=2005
'76':'ACTION_SMALL_STEP_BACK', # jumps a few seconds back during playback of movie. Can b used in videoFullScreen.xml window id=2005

'77':'ACTION_PLAYER_FORWARD', # FF in current file played. global action, can be used anywhere
'78':'ACTION_PLAYER_REWIND', # RW in current file played. global action, can be used anywhere
'79':'ACTION_PLAYER_PLAY', # Play current song. Unpauses song and sets playspeed to 1x. global action, can be used anywhere

'80':'ACTION_DELETE_ITEM', # delete current selected item. Can be used in myfiles.xml window id=3 and in myvideoTitle.xml window id=25
'81':'ACTION_COPY_ITEM', # copy current selected item. Can be used in myfiles.xml window id=3
'82':'ACTION_MOVE_ITEM', # move current selected item. Can be used in myfiles.xml window id=3
'83':'ACTION_SHOW_MPLAYER_OSD', # toggles mplayers OSD. Can be used in videofullscreen.xml window id=2005
'84':'ACTION_OSD_HIDESUBMENU', # removes an OSD sub menu. Can be used in videoOSD.xml window id=2901
'85':'ACTION_TAKE_SCREENSHOT', # take a screenshot
'87':'ACTION_RENAME_ITEM', # rename item

'88':'ACTION_VOLUME_UP',
'89':'ACTION_VOLUME_DOWN',
'91':'ACTION_MUTE',
'92':'ACTION_NAV_BACK',

'100':'ACTION_MOUSE_START',
'100':'ACTION_MOUSE_LEFT_CLICK',
'101':'ACTION_MOUSE_RIGHT_CLICK',
'102':'ACTION_MOUSE_MIDDLE_CLICK',
'103':'ACTION_MOUSE_DOUBLE_CLICK',
'104':'ACTION_MOUSE_WHEEL_UP',
'105':'ACTION_MOUSE_WHEEL_DOWN',
'106':'ACTION_MOUSE_DRAG',
'107':'ACTION_MOUSE_MOVE',
'109':'ACTION_MOUSE_END',

'110':'ACTION_BACKSPACE',
'111':'ACTION_SCROLL_UP',
'112':'ACTION_SCROLL_DOWN',
'113':'ACTION_ANALOG_FORWARD',
'114':'ACTION_ANALOG_REWIND',

'115':'ACTION_MOVE_ITEM_UP', # move item up in playlist
'116':'ACTION_MOVE_ITEM_DOWN', # move item down in playlist
'117':'ACTION_CONTEXT_MENU', # pops up the context menu


# stuff for virtual keyboard shortcuts
'118':'ACTION_SHIFT',
'119':'ACTION_SYMBOLS',
'120':'ACTION_CURSOR_LEFT',
'121':'ACTION_CURSOR_RIGHT',

'122':'ACTION_BUILT_IN_FUNCTION',

'123':'ACTION_SHOW_OSD_TIME', # displays current time, can be used in videoFullScreen.xml window id=2005
'124':'ACTION_ANALOG_SEEK_FORWARD', # seeks forward, and displays the seek bar.
'125':'ACTION_ANALOG_SEEK_BACK', # seeks backward, and displays the seek bar.

'126':'ACTION_VIS_PRESET_SHOW',
'127':'ACTION_VIS_PRESET_LIST',
'128':'ACTION_VIS_PRESET_NEXT',
'129':'ACTION_VIS_PRESET_PREV',
'130':'ACTION_VIS_PRESET_LOCK',
'131':'ACTION_VIS_PRESET_RANDOM',
'132':'ACTION_VIS_RATE_PRESET_PLUS',
'133':'ACTION_VIS_RATE_PRESET_MINUS',

'134':'ACTION_SHOW_VIDEOMENU',
'135':'ACTION_ENTER',

'136':'ACTION_INCREASE_RATING',
'137':'ACTION_DECREASE_RATING',

'138':'ACTION_NEXT_SCENE', # switch to next scene/cutpoint in movie
'139':'ACTION_PREV_SCENE', # switch to previous scene/cutpoint in movie

'140':'ACTION_NEXT_LETTER', # jump through a list or container by letter
'141':'ACTION_PREV_LETTER',

'142':'ACTION_JUMP_SMS2', # jump direct to a particular letter using SMS-style input
'143':'ACTION_JUMP_SMS3',
'144':'ACTION_JUMP_SMS4',
'145':'ACTION_JUMP_SMS5',
'146':'ACTION_JUMP_SMS6',
'147':'ACTION_JUMP_SMS7',
'148':'ACTION_JUMP_SMS8',
'149':'ACTION_JUMP_SMS9',

'150':'ACTION_FILTER_CLEAR',
'151':'ACTION_FILTER_SMS2',
'152':'ACTION_FILTER_SMS3',
'153':'ACTION_FILTER_SMS4',
'154':'ACTION_FILTER_SMS5',
'155':'ACTION_FILTER_SMS6',
'156':'ACTION_FILTER_SMS7',
'157':'ACTION_FILTER_SMS8',
'158':'ACTION_FILTER_SMS9',

'159':'ACTION_FIRST_PAGE',
'160':'ACTION_LAST_PAGE',

'161':'ACTION_AUDIO_DELAY',
'162':'ACTION_SUBTITLE_DELAY',

'180':'ACTION_PASTE',
'181':'ACTION_NEXT_CONTROL',
'182':'ACTION_PREV_CONTROL',
'183':'ACTION_CHANNEL_SWITCH',

'199':'ACTION_TOGGLE_FULLSCREEN', # switch 2 desktop resolution
'200':'ACTION_TOGGLE_WATCHED', # Toggle watched status (videos)
'201':'ACTION_SCAN_ITEM', # scan item
'202':'ACTION_TOGGLE_DIGITAL_ANALOG', # switch digital <-> analog
'203':'ACTION_RELOAD_KEYMAPS', # reloads CButtonTranslator's keymaps
'204':'ACTION_GUIPROFILE_BEGIN', # start the GUIControlProfiler running

'215':'ACTION_TELETEXT_RED', # Teletext Color buttons to control TopText
'216':'ACTION_TELETEXT_GREEN', # " " " " " "
'217':'ACTION_TELETEXT_YELLOW', # " " " " " "
'218':'ACTION_TELETEXT_BLUE', # " " " " " "

'219':'ACTION_INCREASE_PAR',
'220':'ACTION_DECREASE_PAR',

'221':'ACTION_GESTURE_NOTIFY',
'222':'ACTION_GESTURE_BEGIN',
'223':'ACTION_GESTURE_ZOOM', #sendaction with point and currentPinchScale (fingers together < 1.0 -> fingers apart > 1.0)
'224':'ACTION_GESTURE_ROTATE',
'225':'ACTION_GESTURE_PAN',
'226':'ACTION_GESTURE_END',
'227':'ACTION_VSHIFT_UP', # shift up video image in DVDPlayer
'228':'ACTION_VSHIFT_DOWN', # shift down video image in DVDPlayer

'229':'ACTION_PLAYER_PLAYPAUSE', # Play/pause. If playing it pauses, if paused it plays.

# The NOOP action can be specified to disable an input event. This is
# useful in user keyboard.xml etc to disable actions specified in the
# system mappings.
'999':'ACTION_NOOP',

'230':'ACTION_SUBTITLE_VSHIFT_UP', # shift up subtitles in DVDPlayer
'231':'ACTION_SUBTITLE_VSHIFT_DOWN', # shift down subtitles in DVDPlayer
'232':'ACTION_SUBTITLE_ALIGN', # toggle vertical alignment of subtitles

# Window ID defines to make the code a bit more readable
'9999':'WINDOW_INVALID',
'10000':'WINDOW_HOME',
'10001':'WINDOW_PROGRAMS',
'10002':'WINDOW_PICTURES',
'10003':'WINDOW_FILES',
'10004':'WINDOW_SETTINGS_MENU',
'10005':'WINDOW_MUSIC', # virtual window to return the music start window.
'10006':'WINDOW_VIDEOS',
'10007':'WINDOW_SYSTEM_INFORMATION',
'10008':'WINDOW_TEST_PATTERN',
'10011':'WINDOW_SCREEN_CALIBRATION',

'10012':'WINDOW_SETTINGS_MYPICTURES',
'10013':'WINDOW_SETTINGS_MYPROGRAMS',
'10014':'WINDOW_SETTINGS_MYWEATHER',
'10015':'WINDOW_SETTINGS_MYMUSIC',
'10016':'WINDOW_SETTINGS_SYSTEM',
'10017':'WINDOW_SETTINGS_MYVIDEOS',
'10018':'WINDOW_SETTINGS_NETWORK',
'10019':'WINDOW_SETTINGS_APPEARANCE',

'10020':'WINDOW_SCRIPTS', # virtual window for backward compatibility

'10024':'WINDOW_VIDEO_FILES',
'10025':'WINDOW_VIDEO_NAV',
'10028':'WINDOW_VIDEO_PLAYLIST',

'10029':'WINDOW_LOGIN_SCREEN',
'10034':'WINDOW_SETTINGS_PROFILES',

'10040':'WINDOW_ADDON_BROWSER',

'10099':'WINDOW_DIALOG_POINTER',
'10100':'WINDOW_DIALOG_YES_NO',
'10101':'WINDOW_DIALOG_PROGRESS',
'10103':'WINDOW_DIALOG_KEYBOARD',
'10104':'WINDOW_DIALOG_VOLUME_BAR',
'10105':'WINDOW_DIALOG_SUB_MENU',
'10106':'WINDOW_DIALOG_CONTEXT_MENU',
'10107':'WINDOW_DIALOG_KAI_TOAST',
'10109':'WINDOW_DIALOG_NUMERIC',
'10110':'WINDOW_DIALOG_GAMEPAD',
'10111':'WINDOW_DIALOG_BUTTON_MENU',
'10112':'WINDOW_DIALOG_MUSIC_SCAN',
'10113':'WINDOW_DIALOG_MUTE_BUG',
'10114':'WINDOW_DIALOG_PLAYER_CONTROLS',
'10115':'WINDOW_DIALOG_SEEK_BAR',
'10120':'WINDOW_DIALOG_MUSIC_OSD',
'10121':'WINDOW_DIALOG_VIS_SETTINGS',
'10122':'WINDOW_DIALOG_VIS_PRESET_LIST',
'10123':'WINDOW_DIALOG_VIDEO_OSD_SETTINGS',
'10124':'WINDOW_DIALOG_AUDIO_OSD_SETTINGS',
'10125':'WINDOW_DIALOG_VIDEO_BOOKMARKS',
'10126':'WINDOW_DIALOG_FILE_BROWSER',
'10128':'WINDOW_DIALOG_NETWORK_SETUP',
'10129':'WINDOW_DIALOG_MEDIA_SOURCE',
'10130':'WINDOW_DIALOG_PROFILE_SETTINGS',
'10131':'WINDOW_DIALOG_LOCK_SETTINGS',
'10132':'WINDOW_DIALOG_CONTENT_SETTINGS',
'10133':'WINDOW_DIALOG_VIDEO_SCAN',
'10134':'WINDOW_DIALOG_FAVOURITES',
'10135':'WINDOW_DIALOG_SONG_INFO',
'10136':'WINDOW_DIALOG_SMART_PLAYLIST_EDITOR',
'10137':'WINDOW_DIALOG_SMART_PLAYLIST_RULE',
'10138':'WINDOW_DIALOG_BUSY',
'10139':'WINDOW_DIALOG_PICTURE_INFO',
'10140':'WINDOW_DIALOG_ADDON_SETTINGS',
'10141':'WINDOW_DIALOG_ACCESS_POINTS',
'10142':'WINDOW_DIALOG_FULLSCREEN_INFO',
'10143':'WINDOW_DIALOG_KARAOKE_SONGSELECT',
'10144':'WINDOW_DIALOG_KARAOKE_SELECTOR',
'10145':'WINDOW_DIALOG_SLIDER',
'10146':'WINDOW_DIALOG_ADDON_INFO',
'10147':'WINDOW_DIALOG_TEXT_VIEWER',
'10148':'WINDOW_DIALOG_PLAY_EJECT',
'10149':'WINDOW_DIALOG_PERIPHERAL_MANAGER',
'10150':'WINDOW_DIALOG_PERIPHERAL_SETTINGS',

'10500':'WINDOW_MUSIC_PLAYLIST',
'10501':'WINDOW_MUSIC_FILES',
'10502':'WINDOW_MUSIC_NAV',
'10503':'WINDOW_MUSIC_PLAYLIST_EDITOR',

'10600':'WINDOW_DIALOG_OSD_TELETEXT',

#'11000':'WINDOW_VIRTUAL_KEYBOARD',
'12000':'WINDOW_DIALOG_SELECT',
'12001':'WINDOW_DIALOG_MUSIC_INFO',
'12002':'WINDOW_DIALOG_OK',
'12003':'WINDOW_DIALOG_VIDEO_INFO',
'12005':'WINDOW_FULLSCREEN_VIDEO',
'12006':'WINDOW_VISUALISATION',
'12007':'WINDOW_SLIDESHOW',
'12008':'WINDOW_DIALOG_FILESTACKING',
'12009':'WINDOW_KARAOKELYRICS',
'12600':'WINDOW_WEATHER',
'12900':'WINDOW_SCREENSAVER',
'12901':'WINDOW_DIALOG_VIDEO_OSD',

'12902':'WINDOW_VIDEO_MENU',
'12903':'WINDOW_DIALOG_MUSIC_OVERLAY',
'12904':'WINDOW_DIALOG_VIDEO_OVERLAY',
'12905':'WINDOW_VIDEO_TIME_SEEK', # virtual window for time seeking during fullscreen video

'12998':'WINDOW_START', # first window to load
'12999':'WINDOW_STARTUP_ANIM', # for startup animations

# WINDOW_ID's from 13000 to 13099 reserved for Python

'13000':'WINDOW_PYTHON_START',
'13099':'WINDOW_PYTHON_END',
}
            import socket
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            s.settimeout(3)
            s.bind(('', self.pluginConfig['Broadcast']['port']))
            print 'XBMC2: Listening for XBMC broadcast events'
            while not stopBroadcastEvents.isSet():
                #s.settimeout(None)
                message = ''
                addr = ''
                try:
                    message, addr = s.recvfrom(4096)
                except socket.timeout:
                    #print "XBMC2: Broadcast timeout"
                    continue
                except socket.error:
                    eg.PrintError('XBMC2: socket.error: ')
                    import sys, traceback
                    traceback.print_exc()
                    continue
                except:
                    eg.PrintError('XBMC2: Error: get1: ')
                    import sys, traceback
                    traceback.print_exc()
                    continue
                if self.pluginConfig['logRawEvents']:
                    print "XBMC2: Raw event: %s %s" % (repr(message), repr(addr))
                if self.pluginConfig['XBMC']['ip'] != addr[0]:
                    if self.pluginConfig['XBMC']['ip'] == '127.0.0.1':
                        if not addr[0] in socket.gethostbyname_ex('')[2]: continue
                    else:
                        continue
                """
                if self.pluginConfig['Broadcast']['workaround']:
                    s.settimeout(0)
                    try:
                        message2 = ''
                        addr2 = ''
                        if self.pluginConfig['logRawEvents']:
                            message2, addr2 = s.recvfrom(4096)
                            print "XBMC2: Raw event2: %s %s" % (repr(message2), repr(addr2))
                        else:
                            s.recvfrom(4096)
                    except:
                        eg.PrintError('XBMC2: Error: get2')
                    try:
                        message2 = ''
                        addr2 = ''
                        if self.pluginConfig['logRawEvents']:
                            message2, addr2 = s.recvfrom(4096)
                            print "XBMC2: Raw event3: %s %s" % (repr(message2), repr(addr2))
                        else:
                            s.recvfrom(4096)
                    except:
                        eg.PrintError('XBMC2: Error: get3')
                """
                import re
                parts = re.sub('<[^<]+?>', '', message).split(';', 1)
                try:
                    event, payload = parts[0].split(':', 1)
                    if event != 'OnAction':
                        event += '.' + payload.split(':', 1)[0]
                    else:
                        try:
                            event += '.' + ActionList[payload.split(':', 1)[0]]
                        except:
                            event += '.' + payload.split(':', 1)[0]
                    try:
                        payload = unicode(payload.split(':', 1)[1], 'UTF8')
                    except:
                        payload = None
                except:
                    event = parts[0].split(':', 1)[0]
                    payload = None
                if not stopBroadcastEvents.isSet():
                    self.TriggerEvent('Broadcast.' + event, payload)

            s.close()
            print 'XBMC2: Not listening for XBMC broadcast events'