2011-12-10 21:55:00 +01:00
|
|
|
#!/usr/bin/env python
|
2011-12-12 19:27:15 +01:00
|
|
|
# coding: utf-8
|
2011-12-10 21:55:00 +01:00
|
|
|
#
|
|
|
|
# xbmc-wiimote - XBMC event client for Wii remotes
|
|
|
|
#
|
|
|
|
# Copyright © 2011 Paul van Tilburg <paul@luon.net>
|
|
|
|
#
|
|
|
|
# XBMC Wiimote Gateway 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 3 of the License, or (at
|
|
|
|
# your option) any later version.
|
|
|
|
#
|
|
|
|
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import cwiid
|
2011-12-16 22:00:29 +01:00
|
|
|
import os
|
2011-12-10 21:55:00 +01:00
|
|
|
import sys
|
2011-12-16 22:00:29 +01:00
|
|
|
import getopt
|
2011-12-10 21:55:00 +01:00
|
|
|
from time import *
|
|
|
|
from xbmc.xbmcclient import XBMCClient, PacketBUTTON
|
|
|
|
from threading import Thread, Event, Timer
|
|
|
|
|
|
|
|
### Resettable timer code, from http://code.activestate.com/recipes/577407/,
|
|
|
|
### with comments and debug statements removed.
|
|
|
|
class TimerReset(Thread):
|
|
|
|
def __init__(self, interval, function, args=[], kwargs={}):
|
|
|
|
Thread.__init__(self)
|
|
|
|
self.interval = interval
|
|
|
|
self.function = function
|
|
|
|
self.args = args
|
|
|
|
self.kwargs = kwargs
|
|
|
|
self.finished = Event()
|
|
|
|
self.resetted = True
|
|
|
|
|
|
|
|
def cancel(self):
|
|
|
|
self.finished.set()
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
while self.resetted:
|
|
|
|
self.resetted = False
|
|
|
|
self.finished.wait(self.interval)
|
|
|
|
if not self.finished.isSet():
|
|
|
|
self.function(*self.args, **self.kwargs)
|
|
|
|
self.finished.set()
|
|
|
|
|
|
|
|
def reset(self, interval=None):
|
|
|
|
if interval:
|
|
|
|
self.interval = interval
|
|
|
|
self.resetted = True
|
|
|
|
self.finished.set()
|
|
|
|
self.finished.clear()
|
|
|
|
### end of included code
|
|
|
|
|
2012-10-19 20:42:38 +02:00
|
|
|
class Pinger(Thread):
|
|
|
|
def run(self):
|
|
|
|
global xbmc
|
|
|
|
while(True):
|
|
|
|
sleep(55)
|
|
|
|
xbmc.ping()
|
|
|
|
|
2011-12-10 21:55:00 +01:00
|
|
|
# Default settings
|
|
|
|
PROGRAM = "XBMC Wiimote Gateway"
|
|
|
|
ICON = "/usr/share/pixmaps/xbmc/bluetooth.png"
|
2019-08-20 21:41:22 +02:00
|
|
|
WIIMOTE_MAP = "CC:WiiRemote"
|
2011-12-10 21:55:00 +01:00
|
|
|
WIIMOTE_CONVERT = { 1: cwiid.BTN_UP,
|
|
|
|
2: cwiid.BTN_DOWN,
|
|
|
|
3: cwiid.BTN_LEFT,
|
|
|
|
4: cwiid.BTN_RIGHT,
|
|
|
|
5: cwiid.BTN_A,
|
|
|
|
6: cwiid.BTN_B,
|
|
|
|
7: cwiid.BTN_MINUS,
|
|
|
|
8: cwiid.BTN_HOME,
|
|
|
|
9: cwiid.BTN_PLUS,
|
|
|
|
10: cwiid.BTN_1,
|
|
|
|
11: cwiid.BTN_2}
|
|
|
|
|
|
|
|
# Extend XBMCClient with a method to directly send WiiMote button codes.
|
|
|
|
def send_wiimote_button(self, code=0, map_name=WIIMOTE_MAP, queue=0, repeat=1):
|
|
|
|
packet = PacketBUTTON(code=int(code), map_name=str(map_name),
|
|
|
|
queue=queue, repeat=repeat)
|
|
|
|
packet.send(self.sock, self.addr, self.uid)
|
|
|
|
|
|
|
|
XBMCClient.send_wiimote_button = send_wiimote_button
|
|
|
|
|
|
|
|
def rumble_signal(wm):
|
|
|
|
wm.rumble = 1
|
|
|
|
for i in range(0, 3):
|
|
|
|
sleep(0.1)
|
|
|
|
wm.rumble = (i % 2)
|
|
|
|
|
|
|
|
def convert_msg_to_buttons(mesg):
|
|
|
|
buttons = []
|
|
|
|
for button in WIIMOTE_CONVERT.keys():
|
|
|
|
if mesg & WIIMOTE_CONVERT[button]:
|
|
|
|
buttons.append(button)
|
|
|
|
return buttons
|
|
|
|
|
|
|
|
def handle_msg(mesg_list, time):
|
|
|
|
global xbmc, wm, tim
|
|
|
|
for mesg in mesg_list:
|
|
|
|
if mesg[0] == cwiid.MESG_BTN:
|
|
|
|
print("Handling keycode %d" % mesg[1])
|
|
|
|
tim.reset()
|
|
|
|
if (mesg[1] == 0):
|
|
|
|
xbmc.send_wiimote_button(0, repeat=0)
|
|
|
|
else:
|
|
|
|
# FIXME: How to send combined key presses?
|
|
|
|
for button in convert_msg_to_buttons(mesg[1]):
|
|
|
|
xbmc.send_wiimote_button(button)
|
|
|
|
elif mesg[0] == cwiid.MESG_ERROR:
|
|
|
|
wm.close()
|
|
|
|
tim.cancel()
|
|
|
|
if (mesg[1] == cwiid.ERROR_DISCONNECT):
|
|
|
|
print("Wiimote disconnect to to power-off")
|
|
|
|
xbmc.send_notification(PROGRAM, "Wiimote disconnected")
|
|
|
|
elif (mesg[1] == cwiid.ERROR_COMM):
|
|
|
|
print("Wiimote communication error!")
|
|
|
|
xbmc.send_notification(PROGRAM, "Wiimote communication error!")
|
|
|
|
else:
|
|
|
|
print("Unhandled and ignored message type: %d" % mesg[0])
|
|
|
|
|
2011-12-16 22:00:29 +01:00
|
|
|
def timeout_msg():
|
2011-12-10 21:55:00 +01:00
|
|
|
print("Timeout due to inactivity!")
|
|
|
|
|
2011-12-16 22:00:29 +01:00
|
|
|
def usage():
|
|
|
|
print("Usage: xbmc-wiimote [OPTION]...\n"
|
|
|
|
"XBMC event client that receives input from a Wii remote via bluetooth and\n"
|
|
|
|
"feeds them to XBMC.\n\n"
|
|
|
|
"Available options:\n"
|
2011-12-17 16:58:16 +01:00
|
|
|
" -b, --bt-address=ADDRESS\tBluetooth address Wii remote\n"
|
|
|
|
" -l, --led-pattern=PATTERN\tLed patteren when connected (e.g. 1001)\n"
|
2011-12-16 22:00:29 +01:00
|
|
|
" -t, --timeout=TIME\t\tAuto-disconnect timeout\n"
|
|
|
|
" -h, --help\t\t\tDisplay this help and exit\n"
|
|
|
|
"Setting the bluetooth address is mandatory.")
|
|
|
|
|
2011-12-10 21:55:00 +01:00
|
|
|
def main():
|
2011-12-16 22:00:29 +01:00
|
|
|
# Define and get the commandline options.
|
|
|
|
progname = os.path.basename(sys.argv[0])
|
|
|
|
try:
|
|
|
|
opts, args = getopt.getopt(sys.argv[1:],
|
|
|
|
"hb:l:t:",
|
|
|
|
["help", "bt-address=", "led-pattern=", "timeout="])
|
|
|
|
except getopt.GetoptError, err:
|
|
|
|
print "%s: %s." % (progname, err)
|
|
|
|
print "Try `%s --help` for more information." % progname
|
|
|
|
sys.exit(2)
|
|
|
|
|
|
|
|
# Option parsing and defaults.
|
|
|
|
led_pattern = 0b1001
|
|
|
|
timeout = 120
|
|
|
|
bt_address = None
|
|
|
|
for opt, arg in opts:
|
|
|
|
if opt in ("-h", "--help"):
|
|
|
|
usage()
|
|
|
|
sys.exit()
|
|
|
|
elif opt in ("-b", "--bt-address"):
|
|
|
|
bt_address = arg
|
|
|
|
elif opt in ("-l", "--led-pattern"):
|
|
|
|
led_pattern = int(arg[::-1], 2)
|
|
|
|
elif opt in ("-t", "--timeout"):
|
|
|
|
timeout = int(arg)
|
|
|
|
else:
|
|
|
|
assert False, "unhandled option: %s" % opt
|
|
|
|
if not bt_address:
|
|
|
|
print "%s: the bluetooth address is not set!" % progname
|
|
|
|
print "Try `%s --help` for more information." % progname
|
|
|
|
sys.exit(3)
|
|
|
|
|
2011-12-10 21:55:00 +01:00
|
|
|
# Create an XBMCClient object and connect.
|
|
|
|
global xbmc, wm, tim
|
|
|
|
xbmc = XBMCClient(PROGRAM, icon_file=ICON)
|
|
|
|
xbmc.connect()
|
|
|
|
|
2012-10-19 20:42:38 +02:00
|
|
|
# Start the pinger thread to frequently let XBMC know we are still here.
|
|
|
|
ping = Pinger()
|
|
|
|
ping.start()
|
|
|
|
|
2011-12-10 21:55:00 +01:00
|
|
|
while(True):
|
|
|
|
# The main loop.
|
|
|
|
try:
|
2011-12-16 22:00:29 +01:00
|
|
|
print("Press 1+2 on the Wiimote with address %s..." % bt_address)
|
|
|
|
wm = cwiid.Wiimote(bt_address, cwiid.FLAG_REPEAT_BTN |
|
|
|
|
cwiid.FLAG_MESG_IFC)
|
2011-12-10 21:55:00 +01:00
|
|
|
except RuntimeError as error:
|
|
|
|
print(error)
|
|
|
|
continue
|
|
|
|
# If a Wiimote is found, set the leds, rumble, register the
|
|
|
|
# callback and start the timer.
|
2011-12-16 22:00:29 +01:00
|
|
|
tim = TimerReset(timeout, timeout_msg)
|
2011-12-10 21:55:00 +01:00
|
|
|
tim.start()
|
2011-12-16 22:00:29 +01:00
|
|
|
wm.led = led_pattern
|
2011-12-10 21:55:00 +01:00
|
|
|
wm.rpt_mode = cwiid.RPT_BTN
|
|
|
|
wm.mesg_callback = handle_msg
|
|
|
|
rumble_signal(wm)
|
|
|
|
|
|
|
|
battperc = 100.0 * wm.state['battery'] / cwiid.BATTERY_MAX
|
|
|
|
print("Wiimote connected (battery: %d%%)" % battperc)
|
|
|
|
xbmc.send_notification(PROGRAM,
|
2012-10-19 20:43:18 +02:00
|
|
|
"Wiimote connected (battery: %d%%)" % battperc)
|
2011-12-10 21:55:00 +01:00
|
|
|
tim.join()
|
|
|
|
# A timeout has occured (due to inactivity), close the connection to
|
|
|
|
# the Wiimote.
|
|
|
|
try:
|
|
|
|
wm.close()
|
|
|
|
except ValueError:
|
|
|
|
# Connection was already closed.
|
|
|
|
pass
|
|
|
|
print("Wiimote disconnected due to timeout")
|
|
|
|
#xbmc.send_notification(PROGRAM, "Wiimote disconnected")
|
|
|
|
sleep(3)
|
|
|
|
|
|
|
|
# Ok we're done, close the connection.
|
|
|
|
xbmc.close()
|
|
|
|
|
|
|
|
if __name__=="__main__":
|
|
|
|
main()
|