plugins/HauppaugeIR/__init__.py
# -*- coding: utf-8 -*-
#
# plugins/HauppaugeIR/__init__.py
#
# Copyright (C) 2008 Stefan Gollmer
#
# This file is a plugin for EventGhost.
# Copyright © 2005-2020 EventGhost Project <http://www.eventghost.net/>
#
# 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, see <http://www.gnu.org/licenses/>.
eg.RegisterPlugin(
name = "Hauppauge IR",
author = "Stefan Gollmer",
version = "1.04.1093",
kind = "remote",
guid = "{B54E7F32-E117-4C24-A892-9D69BC290568}",
description = (
'Hardware plugin for the '
'<a href="http://www.hauppauge.com">'
'Hauppauge IR Control</a>, '
'delivered with several Hauppauge TV cards'
),
help = (
'This plugin is using the file "irremote.DLL" which is located in '
'the installation path of the Hauppauge IR software. The version '
'of this dll must be at least 2.45.<br><br>'
'The Hauppauge program "Ir.exe" should not be started anymore.'
),
url = "http://www.eventghost.net/forum/viewtopic.php?t=821",
icon = (
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAaElEQVR42mNkoBAwYhH7"
"DyIcHBwYDhw4AKdxqWfEphlmAAwgGYChB6cBxLoawwCYzQ8ePGBQUFCA00guwW9AQ0MD"
"XDFyOIAAVI7GXqDYBRSHwTAKRGyAKBfg1g40gBgXUOoFQoZgqAcAnwlSETUFcqwAAAAA"
"SUVORK5CYII="
)
)
import os
from threading import Timer, Lock
from time import sleep, time
from msvcrt import get_osfhandle
import _winreg
from threading import Event
from eg.WinApi.Dynamic import (
WinDLL,
windll,
WINFUNCTYPE,
POINTER,
HWND ,
HMODULE ,
c_int,
c_uint,
c_byte,
c_void_p,
c_char_p,
c_ulong,
WNDCLASS,
TIMERPROC,
GetDesktopWindow,
WNDPROC,
CreateWindow,
WinError,
byref,
WM_TIMER,
RegisterClass,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
WM_TIMER,
pointer,
DestroyWindow,
UnregisterClass,
GetTickCount,
DWORD,
)
#c_int WINAPI IR_Open(HWND hWnd, c_int, c_byte, c_int)
#------------------------------------------
#To register the window that will receives messages on keystroke events (from Hauppauge IR only)
# and enables the receiver
#
#hWnd : handle to the window which will receives WM_USER messages
#returns value != 0 is successfull
#
#
#c_int WINAPI IR_Close(HWND hWnd, c_int)
#------------------------------------------
#To unregister the window that will receives messages on keystroke events (from Hauppauge IR only)
# and disables the receiver
#
#hWnd : handle to the window which will receives WM_USER messages
#returns value != 0 is successfull
#
#
#c_int WINAPI IR_GetSystemKeyCode(POINTER( c_int) repeatCode, POINTER( c_int) systemCode, POINTER( c_int) keycCode )
#------------------------------------------
#To unregister the window that will receives messages on keystroke events (from Hauppauge IR only)
# and disables the receiver
#
#repeatCode : return value containing 0, if the key is pressed continues, otherwise 2. Not working fine, not usable
#systemCode : return value containing the system code of the remote control (0,30,31,0x800f), see Irremote.ini
#keyCode : return value containing the key code (0-99)
#
#returns value ==1 if key is pressed
#------------------------------------------
#
#
#
#
# If the HauppaugeIR driver couldn't be initialized, the event ghost event
# "HauppaugeIR.InitError"
# will be fired
HCWClassic = {
0: "OLD0",
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
12: "RADIO",
13: "MUTE",
15: "TV",
16: "VOLUP",
17: "VOLDOWN",
30: "RESERVED",
32: "CHNLUP",
33: "CHNLDOWN",
34: "SOURCE",
38: "MINIMIZE",
46: "FULLSCREEN",
}
HCWPVR = {
0:"0",
1:"1",
2:"2",
3:"3",
4:"4",
5:"5",
6:"6",
7:"7",
8:"8",
9:"9",
46:"GREEN",
56:"YELLOW",
11:"RED",
41:"BLUE",
12:"FUNC",
13:"MENU",
15:"MUTE",
16:"VOLUP",
17:"VOLDOWN",
32:"CHNLUP",
33:"CHNLDOWN",
61:"GRNPOWER",
31:"BACK",
37:"OK",
59:"GO",
60:"FULLSCREEN",
55:"REC",
54:"STOP",
48:"PAUSE",
53:"PLAY",
50:"REWIND",
52:"FASTFWD",
30:"SKIPFWD",
36:"SKIPREV",
}
HCWPVR2 = {
0: "0",
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
46: "GREEN",
56: "YELLOW",
11: "RED",
41: "BLUE",
13: "MENU",
15: "MUTE",
16: "VOLUP",
17: "VOLDOWN",
32: "CHNLUP",
33: "CHNLDOWN",
61: "GRNPOWER",
31: "BACK",
37: "OK",
59: "GO",
55: "REC",
54: "STOP",
48: "PAUSE",
53: "PLAY",
50: "REWIND",
52: "FASTFWD",
30: "SKIPFWD",
36: "SKIPREV",
#new to hcwpvr2
12: "RADIO",
28: "TVNEW",
24: "VIDEOS",
25: "MUSIC",
26: "PICTURES",
27: "GUIDE",
22: "NAVLEFT",
23: "NAVRIGHT",
20: "NAVUP",
21: "NAVDOWN",
10: "TEXT",
14: "SUBCC",
18: "CHNLPREV",
}
DSR0112 = {
0: "0",
1: "1",
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
13: "MENU",
15: "MUTE",
16: "VOLUP",
17: "VOLDOWN",
32: "CHNLUP",
33: "CHNLDOWN",
61: "GRNPOWER",
31: "BACK",
37: "OK",
59: "GO",
55: "REC",
54: "STOP",
48: "PAUSE",
53: "PLAY",
50: "REWIND",
52: "FASTFWD",
30: "SKIPFWD",
36: "SKIPREV",
28: "TVNEW",
22: "NAVLEFT",
23: "NAVRIGHT",
20: "NAVUP",
21: "NAVDOWN",
10: "TEXT",
18: "CHNLPREV",
}
HauppaugeIRTable = {
0: HCWClassic ,
29: DSR0112 ,
30: HCWPVR2 ,
31: HCWPVR ,
}
class HauppaugeIRMessageReceiver(eg.ThreadWorker):
"""
A thread with a hidden window to receive win32 messages from the driver
"""
@eg.LogIt
def Setup( self,
plugin,
waitTime,
pollTime,
useDefaultPollTime,
initTime = 15.0,
checkRepeatFlag = False,
repeatReleaseTime = 200 ):
"""
This will be called inside the thread at the beginning.
"""
self.lock = Lock()
self.abort = False
self.plugin = plugin
self.waitTime = float(waitTime)/1000.0
self.repeatReleaseTime = float(repeatReleaseTime)/1000.0
self.pollTime = pollTime
self.useDefaultPollTime = useDefaultPollTime
self.defaultPollTime = -1
self.LastKeyCode = -1
self.checkRepeatFlag = checkRepeatFlag
self.repeatCode = c_int(0)
self.systemCode = c_int(0)
self.keyCode = c_int(0)
self.lastEvent = eg.EventGhostEvent()
self.keyStillPressed = False
self.initTerminated = False
self.timerInit = None
self.timerKey = None
self.hwnd = None
self.dll = None
# load irremote.dll
try:
regHandle = _winreg.OpenKey(
_winreg.HKEY_LOCAL_MACHINE,
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Hauppauge WinTV Infrared Remote',
0,
_winreg.KEY_READ
)
InstallString = _winreg.QueryValueEx(regHandle, 'UninstallString')[0]
_winreg.CloseKey( regHandle )
irremoteDir = InstallString.partition(' ')[0].rsplit('\\',1)[0]
dllPath = os.path.join(irremoteDir, "irremote.DLL")
self.dll = windll.LoadLibrary(dllPath)
self.dll = WinDLL(dllPath)
except:
plugin.PrintError("Couldn't find irremote.dll! Reinstalling the Hauppauge "
"WinTV Infrared Remote package can solve the problem."
)
raise self.plugin.Exceptions.DeviceNotFound
self.IR_Open = WINFUNCTYPE( c_int, HWND, c_int, c_byte, c_int )
self.IR_Close = WINFUNCTYPE( c_int, HWND, c_int )
self.IR_GetSystemKeyCode = WINFUNCTYPE( c_int, POINTER( c_int), POINTER( c_int), POINTER( c_int) )
self.TIMERPROC = WINFUNCTYPE( None, HWND, c_uint, c_uint, DWORD )
self.IR_Open = self.dll.IR_Open
self.IR_Close = self.dll.IR_Close
self.IR_GetSystemKeyCode = self.dll.IR_GetSystemKeyCode
wc = WNDCLASS()
wc.hInstance = GetDesktopWindow()
wc.lpszClassName = "HaupPluginEventSinkWndClass"
wc.lpfnWndProc = WNDPROC(self.MyWndProc)
if not RegisterClass(byref(wc)):
raise WinError()
self.hwnd = CreateWindow(
wc.lpszClassName,
"HaupaugePlugin Event Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
wc.hInstance,
None
)
if not self.hwnd:
raise WinError()
self.wc = wc
self.hinst = wc.hInstance
self.timerInit = Timer( initTime, self.PostInit) # Init delayed init timer ( in case of standby problems)
self.timerInit.start()
@eg.LogItWithReturn
def Finish(self):
"""
This will be called inside the thread when it finishes. It will even
be called if the thread exits through an exception.
"""
self.abort = True
self.lock.acquire()
if self.dll:
self.dll.IR_Close(self.hwnd, 0);
#print "Irremote is stopped"
if self.timerInit :
self.timerInit.cancel()
#print "Init aborted"
if self.hwnd:
windll.user32.KillTimer(self.hwnd, 1)
DestroyWindow(self.hwnd)
UnregisterClass(self.wc.lpszClassName, self.hinst)
if self.defaultPollTime != -1 :
regHandle = _winreg.OpenKey(
_winreg.HKEY_LOCAL_MACHINE,
'SOFTWARE\hauppauge\IR',
0,
_winreg.KEY_WRITE | _winreg.KEY_READ
)
_winreg.SetValueEx( regHandle, 'PollRate', 0, _winreg.REG_DWORD, int(self.defaultPollTime) )
_winreg.CloseKey( regHandle )
self.lock.release()
def MyWndProc(self, hwnd, mesg, wParam, lParam) :
if mesg == WM_TIMER:
keyHit = self.IR_GetSystemKeyCode(byref(self.repeatCode), byref(self.systemCode), byref(self.keyCode) );
if keyHit == 1:
#print "RepeatCode = ", self.repeatCode, " systemCode = ", self.systemCode, " keyCode = ", self.keyCode, " time = ", time()
if self.timerKey :
self.timerKey.cancel()
key = self.keyCode.value
repeat = ( self.repeatCode.value == 0 )
if ( not repeat and self.checkRepeatFlag ) or not ( self.keyStillPressed and self.LastKeyCode == key ) :
self.LastKeyCode = key
if not self.systemCode.value in HauppaugeIRTable:
eventString = "%d" % key
elif key in HauppaugeIRTable[ self.systemCode.value ]:
eventString = HauppaugeIRTable[ self.systemCode.value ][key]
else:
eventString = "%d" % key
self.lastEvent = self.plugin.TriggerEnduringEvent(eventString)
self.keyStillPressed = True
releaseTime = self.waitTime
else :
releaseTime = self.repeatReleaseTime
self.timerKey = Timer(releaseTime, self.OnTimeOut)
self.timerKey.start()
return 1
def OnTimeOut(self):
#print "Release Key"
self.keyStillPressed = False
self.lastEvent.SetShouldEnd()
@eg.LogIt
def PostInit( self ) :
self.lock.acquire()
if self.abort :
self.lock.release()
return
self.timerInit = Timer( 60.0, self.OnPostInitTimeOut) # Init timeout timer
self.timerInit.start()
returnVal = False
if ( not self.useDefaultPollTime ) :
regHandle = _winreg.OpenKey(
_winreg.HKEY_LOCAL_MACHINE,
'SOFTWARE\hauppauge\IR',
0,
_winreg.KEY_WRITE | _winreg.KEY_READ
)
self.defaultPollTime = int( _winreg.QueryValueEx(regHandle, 'PollRate')[0] )
_winreg.SetValueEx( regHandle, 'PollRate', 0, _winreg.REG_DWORD, int(self.pollTime) )
returnVal = self.IR_Open(self.hwnd, 0, 0, 0);
_winreg.SetValueEx( regHandle, 'PollRate', 0, _winreg.REG_DWORD, int(self.defaultPollTime) )
_winreg.CloseKey( regHandle )
self.defaultPollTime = -1
else :
returnVal = self.IR_Open(self.hwnd, 0, 0, 0);
self.timerInit.cancel()
if not self.initTerminated :
if not returnVal and not self.initTerminated :
self.plugin.PrintError("Couldn't start the Hauppauge remote control")
self.Stop()
self.plugin.TriggerEvent("InitError")
else :
#print "Irremote is started"
pass
del self.timerInit
self.timerInit = None
self.lock.release()
@eg.LogIt
def OnPostInitTimeOut( self ) :
self.initTerminated = True
self.plugin.PrintError("Couldn't start the Hauppauge remote control")
self.Stop()
self.plugin.TriggerEvent("InitError")
class HauppaugeIR(eg.PluginClass):
class text:
buttonTimeout = "Button release timeout (seconds):"
buttonTimeoutDescr = (
"(If you get unintended double presses of the buttons, "
"increase this value.)"
)
repeatReleaseTime = "Button release timeout (seconds) after repeat detected:"
pollTime = "Poll time (seconds)"
buttonPollTime = "Use default poll time (0.2s)"
checkRepeatFlag = "Use the repeat flag of the IR"
def __init__( self ) :
self.AddAction(Restart)
self.AddAction(OnComputerSuspend, hidden = True ) # For test only
self.AddAction(OnComputerResume, hidden = True ) # For test only
self.waitTime = -1
self.repeatReleaseTime = -1
self.pollTime = -1
self.initTime = -1
self.useDefaultPollTime = True
self.started = False
self.config = False
def __start__( self,
waitTime=-1,
pollTime = -1,
useDefaultPollTime = True,
checkRepeatFlag = False,
repeatReleaseTime = 200,
initTime = 15.0 ):
if waitTime != -1 :
self.waitTime = waitTime
self.repeatReleaseTime = repeatReleaseTime
self.pollTime = pollTime
self.initTime = initTime
self.useDefaultPollTime = useDefaultPollTime
self.checkRepeatFlag = checkRepeatFlag
if self.config :
initTime = 1.0
self.config = False
else :
initTime = self.initTime
self.msgThread = HauppaugeIRMessageReceiver( self,
self.waitTime,
self.pollTime,
self.useDefaultPollTime,
initTime,
self.checkRepeatFlag,
self.repeatReleaseTime )
self.msgThread.Start( 15.0 )
self.started = True
@eg.LogIt
def OnComputerSuspend(self, suspendType = None):
if self.started :
self.Finish()
@eg.LogIt
def OnComputerResume(self, suspendType = None):
if self.started :
self.__start__()
@eg.LogItWithReturn
def Finish(self, timeout = 10.0 ):
self.msgThread.Stop( timeout )
return True
def __stop__(self ):
self.started = False
self.Finish()
def Configure( self,
waitTime=300,
pollTime = 90,
useDefaultPollTime = True,
checkRepeatFlag = False,
repeatReleaseTime = 200 ):
panel = eg.ConfigPanel(self)
text = self.text
def onCheckBox( event ) :
enable = not useDefaultPollTimeCtrl.GetValue()
pollTimeCtrl.Enable( enable )
event.Skip()
waitTimeCtrl = panel.SpinNumCtrl(waitTime, min=0, max=999, fractionWidth=0, integerWidth=3)
repeatReleaseTimeCtrl = panel.SpinNumCtrl(repeatReleaseTime, min=0, max=999, fractionWidth=0, integerWidth=3)
useDefaultPollTimeCtrl = wx.CheckBox(panel, -1, text.buttonPollTime)
useDefaultPollTimeCtrl.SetValue( useDefaultPollTime )
useDefaultPollTimeCtrl.Bind(wx.EVT_CHECKBOX, onCheckBox)
pollTimeCtrl = panel.SpinNumCtrl(pollTime, min=0, max=999, fractionWidth=0, integerWidth=3)
checkRepeatFlagCtrl = wx.CheckBox(panel, -1, text.checkRepeatFlag)
checkRepeatFlagCtrl.SetValue( checkRepeatFlag )
sizer = wx.GridBagSizer( 5, 5 )
sizer.Add( wx.StaticText( panel, -1, text.buttonTimeout ),
( 0, 0 ),(1,2), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT )
sizer.Add( waitTimeCtrl, ( 0, 2 ), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL )
sizer.Add( wx.StaticText( panel, -1, text.buttonTimeoutDescr ),
( 1, 0 ),(1,3), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL )
sizer.Add( wx.StaticText( panel, -1, text.repeatReleaseTime ),
( 3, 0 ),(1,2), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT )
sizer.Add( repeatReleaseTimeCtrl, ( 3, 2 ), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL )
sizer.Add( useDefaultPollTimeCtrl, ( 5, 0 ), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL )
sizer.Add( wx.StaticText( panel, -1, text.pollTime ),
( 5, 1 ), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT )
sizer.Add( pollTimeCtrl, ( 5, 2 ), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL )
sizer.Add( checkRepeatFlagCtrl, ( 7, 0 ), flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL )
panel.sizer.Add( sizer, 0 )
onCheckBox(wx.CommandEvent())
while panel.Affirmed():
waitTime = waitTimeCtrl.GetValue()
repeatReleaseTime = repeatReleaseTimeCtrl.GetValue()
pollTime = pollTimeCtrl.GetValue()
useDefaultPollTime = useDefaultPollTimeCtrl.GetValue()
checkRepeatFlag = checkRepeatFlagCtrl.GetValue()
panel.SetResult( waitTime, pollTime, useDefaultPollTime, checkRepeatFlag, repeatReleaseTime )
self.config = True
class Restart(eg.ActionClass) :
@eg.LogIt
def __call__( self ) :
self.plugin.__stop__( timeout = 10.0 )
self.plugin.__start__( initTime = 1.0 )
return True
class OnComputerSuspend(eg.ActionClass) :
def __call__( self ) :
self.plugin.OnComputerSuspend()
return True
class OnComputerResume(eg.ActionClass) :
def __call__( self ) :
self.plugin.OnComputerResume()
return True