Made a few dependencies optional and removed others.

This commit is contained in:
Paulus Schoutsen 2013-10-13 09:40:38 -07:00
parent 6de531db0d
commit 785e17e1e6
14 changed files with 75 additions and 1108 deletions

View File

@ -11,25 +11,18 @@ import os
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
import re import re
import webbrowser
import dateutil.parser
from phue import Bridge
import requests import requests
from .packages import pychromecast, pykeyboard import homeassistant as ha
import homeassistant.util as util
from . import track_state_change from homeassistant.observers import (
from .util import sanitize_filename
from .observers import (
STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON,
STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME, STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME,
STATE_CATEGORY_NEXT_SUN_SETTING, track_time_change) STATE_CATEGORY_NEXT_SUN_SETTING)
LIGHT_TRANSITION_TIME = timedelta(minutes=15) LIGHT_TRANSITION_TIME = timedelta(minutes=15)
HUE_MAX_TRANSITION_TIME = 9000 # 900 seconds = 15 minutes
EVENT_DOWNLOAD_FILE = "download_file" EVENT_DOWNLOAD_FILE = "download_file"
EVENT_BROWSE_URL = "browse_url" EVENT_BROWSE_URL = "browse_url"
EVENT_CHROMECAST_YOUTUBE_VIDEO = "chromecast.play_youtube_video" EVENT_CHROMECAST_YOUTUBE_VIDEO = "chromecast.play_youtube_video"
@ -43,7 +36,9 @@ EVENT_KEYBOARD_MEDIA_PLAY_PAUSE = "keyboard.media_play_pause"
def _hue_process_transition_time(transition_seconds): def _hue_process_transition_time(transition_seconds):
""" Transition time is in 1/10th seconds """ Transition time is in 1/10th seconds
and cannot exceed MAX_TRANSITION_TIME. """ and cannot exceed MAX_TRANSITION_TIME. """
return min(HUE_MAX_TRANSITION_TIME, transition_seconds * 10)
# Max transition time for Hue is 900 seconds/15 minutes
return min(9000, transition_seconds * 10)
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
@ -60,18 +55,18 @@ class LightTrigger(object):
# Track home coming of each seperate device # Track home coming of each seperate device
for category in device_tracker.device_state_categories(): for category in device_tracker.device_state_categories():
track_state_change(eventbus, category, ha.track_state_change(eventbus, category,
DEVICE_STATE_NOT_HOME, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME, DEVICE_STATE_HOME,
self._handle_device_state_change) self._handle_device_state_change)
# Track when all devices are gone to shut down lights # Track when all devices are gone to shut down lights
track_state_change(eventbus, STATE_CATEGORY_ALL_DEVICES, ha.track_state_change(eventbus, STATE_CATEGORY_ALL_DEVICES,
DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME,
self._handle_device_state_change) self._handle_device_state_change)
# Track every time sun rises so we can schedule a time-based # Track every time sun rises so we can schedule a time-based
# pre-sun set event # pre-sun set event
track_state_change(eventbus, STATE_CATEGORY_SUN, ha.track_state_change(eventbus, STATE_CATEGORY_SUN,
SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON,
self._handle_sun_rising) self._handle_sun_rising)
@ -109,7 +104,7 @@ class LightTrigger(object):
return lambda now: self._turn_light_on_before_sunset(light_id) return lambda now: self._turn_light_on_before_sunset(light_id)
for index, light_id in enumerate(self.light_control.light_ids): for index, light_id in enumerate(self.light_control.light_ids):
track_time_change(self.eventbus, turn_on(light_id), ha.track_time_change(self.eventbus, turn_on(light_id),
point_in_time=start_point + point_in_time=start_point +
index * LIGHT_TRANSITION_TIME) index * LIGHT_TRANSITION_TIME)
@ -176,7 +171,7 @@ class LightTrigger(object):
def _next_sun_setting(self): def _next_sun_setting(self):
""" Returns the datetime object representing the next sun setting. """ """ Returns the datetime object representing the next sun setting. """
return dateutil.parser.parse( return util.str_to_datetime(
self.statemachine.get_state(STATE_CATEGORY_NEXT_SUN_SETTING).state) self.statemachine.get_state(STATE_CATEGORY_NEXT_SUN_SETTING).state)
def _time_for_light_before_sun_set(self): def _time_for_light_before_sun_set(self):
@ -193,14 +188,21 @@ class HueLightControl(object):
""" Class to interface with the Hue light system. """ """ Class to interface with the Hue light system. """
def __init__(self, host=None): def __init__(self, host=None):
self.bridge = Bridge(host) try:
import phue
except ImportError:
logging.getLogger(__name__).error(("HueLightControl:"
"Unable to init due to missing dependency phue."))
return
self.bridge = phue.Bridge(host)
self.lights = self.bridge.get_light_objects() self.lights = self.bridge.get_light_objects()
self.light_ids = [light.light_id for light in self.lights] self.light_ids = [light.light_id for light in self.lights]
def is_light_on(self, light_id=None): def is_light_on(self, light_id=None):
""" Returns if specified or all light are on. """ """ Returns if specified or all light are on. """
if light_id is None: if not light_id:
return sum([1 for light in self.lights if light.on]) > 0 return sum([1 for light in self.lights if light.on]) > 0
else: else:
@ -209,7 +211,7 @@ class HueLightControl(object):
def turn_light_on(self, light_id=None, transition_seconds=None): def turn_light_on(self, light_id=None, transition_seconds=None):
""" Turn the specified or all lights on. """ """ Turn the specified or all lights on. """
if light_id is None: if not light_id:
light_id = self.light_ids light_id = self.light_ids
command = {'on': True, 'xy': [0.5119, 0.4147], 'bri':164} command = {'on': True, 'xy': [0.5119, 0.4147], 'bri':164}
@ -223,7 +225,7 @@ class HueLightControl(object):
def turn_light_off(self, light_id=None, transition_seconds=None): def turn_light_off(self, light_id=None, transition_seconds=None):
""" Turn the specified or all lights off. """ """ Turn the specified or all lights off. """
if light_id is None: if not light_id:
light_id = self.light_ids light_id = self.light_ids
command = {'on': False} command = {'on': False}
@ -243,7 +245,8 @@ def setup_file_downloader(eventbus, download_path):
if not os.path.isdir(download_path): if not os.path.isdir(download_path):
logger.error( logger.error(
"Download path {} does not exist. File Downloader not active.". ("FileDownloader:"
"Download path {} does not exist. File Downloader not active.").
format(download_path)) format(download_path))
return return
@ -270,7 +273,7 @@ def setup_file_downloader(eventbus, download_path):
filename = "ha_download" filename = "ha_download"
# Remove stuff to ruin paths # Remove stuff to ruin paths
filename = sanitize_filename(filename) filename = util.sanitize_filename(filename)
path, ext = os.path.splitext(os.path.join(download_path, path, ext = os.path.splitext(os.path.join(download_path,
filename)) filename))
@ -303,11 +306,16 @@ def setup_file_downloader(eventbus, download_path):
def setup_webbrowser(eventbus): def setup_webbrowser(eventbus):
""" Listen for browse_url events and open """ Listen for browse_url events and open
the url in the default webbrowser. """ the url in the default webbrowser. """
import webbrowser
eventbus.listen(EVENT_BROWSE_URL, eventbus.listen(EVENT_BROWSE_URL,
lambda event: webbrowser.open(event.data['url'])) lambda event: webbrowser.open(event.data['url']))
def setup_chromecast(eventbus, host): def setup_chromecast(eventbus, host):
""" Listen for chromecast events. """ """ Listen for chromecast events. """
from homeassistant.packages import pychromecast
eventbus.listen("start_fireplace", eventbus.listen("start_fireplace",
lambda event: pychromecast.play_youtube_video(host, "eyU3bRy2x44")) lambda event: pychromecast.play_youtube_video(host, "eyU3bRy2x44"))
@ -319,6 +327,13 @@ def setup_chromecast(eventbus, host):
def setup_media_buttons(eventbus): def setup_media_buttons(eventbus):
""" Listen for keyboard events. """ """ Listen for keyboard events. """
try:
import pykeyboard
except ImportError:
logging.getLogger(__name__).error(("MediaButtons:"
"Unable to setup due to missing dependency PyUserInput."))
return
keyboard = pykeyboard.PyKeyboard() keyboard = pykeyboard.PyKeyboard()
keyboard.special_key_assignment() keyboard.special_key_assignment()

View File

@ -31,7 +31,8 @@ import logging
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from urlparse import urlparse, parse_qs from urlparse import urlparse, parse_qs
from . import EVENT_START import homeassistant
import homeassistant.util as util
SERVER_PORT = 8123 SERVER_PORT = 8123
@ -64,7 +65,7 @@ class HTTPInterface(threading.Thread):
self.server.statemachine = statemachine self.server.statemachine = statemachine
self.server.api_password = api_password self.server.api_password = api_password
eventbus.listen(EVENT_START, lambda event: self.start()) eventbus.listen(homeassistant.EVENT_START, lambda event: self.start())
def run(self): def run(self):
""" Start the HTTP interface. """ """ Start the HTTP interface. """
@ -117,7 +118,7 @@ class RequestHandler(BaseHTTPRequestHandler):
write("<tr><td>{}</td><td>{}</td><td>{}</td></tr>". write("<tr><td>{}</td><td>{}</td><td>{}</td></tr>".
format(category, state, format(category, state,
last_changed.strftime("%H:%M:%S %d-%m-%Y"))) util.datetime_to_str(last_changed)))
write("</table>") write("</table>")

View File

@ -16,9 +16,9 @@ import re
import json import json
import requests import requests
import ephem
from . import track_time_change import homeassistant as ha
import homeassistant.util as util
STATE_CATEGORY_SUN = "weather.sun" STATE_CATEGORY_SUN = "weather.sun"
STATE_CATEGORY_NEXT_SUN_RISING = "weather.next_sun_rising" STATE_CATEGORY_NEXT_SUN_RISING = "weather.next_sun_rising"
@ -45,9 +45,16 @@ KNOWN_DEVICES_FILE = "known_devices.csv"
def track_sun(eventbus, statemachine, latitude, longitude): def track_sun(eventbus, statemachine, latitude, longitude):
""" Tracks the state of the sun. """ """ Tracks the state of the sun. """
logger = logging.getLogger(__name__)
try:
import ephem
except ImportError:
logger.error(("TrackSun:"
"Unable to setup due to missing dependency ephem."))
return
sun = ephem.Sun() # pylint: disable=no-member sun = ephem.Sun() # pylint: disable=no-member
logger = logging.getLogger(__name__)
def update_sun_state(now): # pylint: disable=unused-argument def update_sun_state(now): # pylint: disable=unused-argument
""" Method to update the current state of the sun and """ Method to update the current state of the sun and
@ -72,12 +79,12 @@ def track_sun(eventbus, statemachine, latitude, longitude):
statemachine.set_state(STATE_CATEGORY_SUN, new_state) statemachine.set_state(STATE_CATEGORY_SUN, new_state)
statemachine.set_state(STATE_CATEGORY_NEXT_SUN_RISING, statemachine.set_state(STATE_CATEGORY_NEXT_SUN_RISING,
next_rising.isoformat()) util.datetime_to_str(next_rising))
statemachine.set_state(STATE_CATEGORY_NEXT_SUN_SETTING, statemachine.set_state(STATE_CATEGORY_NEXT_SUN_SETTING,
next_setting.isoformat()) util.datetime_to_str(next_setting))
# +10 seconds to be sure that the change has occured # +10 seconds to be sure that the change has occured
track_time_change(eventbus, update_sun_state, ha.track_time_change(eventbus, update_sun_state,
point_in_time=next_change + timedelta(seconds=10)) point_in_time=next_change + timedelta(seconds=10))
update_sun_state(None) update_sun_state(None)
@ -156,7 +163,7 @@ class DeviceTracker(object):
format(KNOWN_DEVICES_FILE)) format(KNOWN_DEVICES_FILE))
track_time_change(eventbus, ha.track_time_change(eventbus,
lambda time: self.update_devices(device_scanner.scan_devices())) lambda time: self.update_devices(device_scanner.scan_devices()))
def device_state_categories(self): def device_state_categories(self):

View File

@ -7,10 +7,5 @@ PyChromecast
------------ ------------
https://github.com/balloob/pychromecast https://github.com/balloob/pychromecast
PyKeyboard
----------
Forked from https://github.com/SavinaRoja/PyUserInput
Patched with some code to get it working on OS X:
https://github.com/balloob/PyUserInput
""" """

View File

@ -1,37 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.
"""
The goal of PyMouse is to have a cross-platform way to control the mouse.
PyMouse should work on Windows, Mac and any Unix that has xlib.
PyKeyboard is a part of PyUserInput, along with PyMouse, for more information
about this project, see:
http://github.com/SavinaRoja/PyUserInput
"""
import sys
if sys.platform.startswith('java'):
from .java_ import PyKeyboard
elif sys.platform == 'darwin':
from .mac import PyKeyboard, PyKeyboardEvent
elif sys.platform == 'win32':
from .windows import PyKeyboard, PyKeyboardEvent
else:
from .x11 import PyKeyboard, PyKeyboardEvent

View File

@ -1,102 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.
"""
As the base file, this provides a rough operational model along with the
framework to be extended by each platform.
"""
import time
from threading import Thread
class PyKeyboardMeta(object):
"""
The base class for PyKeyboard. Represents basic operational model.
"""
def press_key(self, character=''):
"""Press a given character key."""
raise NotImplementedError
def release_key(self, character=''):
"""Release a given character key."""
raise NotImplementedError
def tap_key(self, character='', n=1, interval=0):
"""Press and release a given character key n times."""
for i in xrange(n):
self.press_key(character)
self.release_key(character)
time.sleep(interval)
def type_string(self, char_string, interval=0):
"""A convenience method for typing longer strings of characters."""
for i in char_string:
time.sleep(interval)
self.tap_key(i)
def special_key_assignment(self):
"""Makes special keys more accessible."""
raise NotImplementedError
def lookup_character_value(self, character):
"""
If necessary, lookup a valid API value for the key press from the
character.
"""
raise NotImplementedError
def is_char_shifted(self, character):
"""Returns True if the key character is uppercase or shifted."""
if character.isupper():
return True
if character in '<>?:"{}|~!@#$%^&*()_+':
return True
return False
class PyKeyboardEventMeta(Thread):
"""
The base class for PyKeyboard. Represents basic operational model.
"""
def __init__(self, capture=False):
Thread.__init__(self)
self.daemon = True
self.capture = capture
self.state = True
def run(self):
self.state = True
def stop(self):
self.state = False
def handler(self):
raise NotImplementedError
def key_press(self, key):
"""Subclass this method with your key press event handler."""
pass
def key_release(self, key):
"""Subclass this method with your key release event handler."""
pass
def escape_code(self):
"""
Defines a means to signal a stop to listening. Subclass this with your
escape behavior.
"""
escape = None
return escape

View File

@ -1,14 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.

View File

@ -1,201 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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 time
from Quartz import *
from AppKit import NSEvent
from .base import PyKeyboardMeta, PyKeyboardEventMeta
# Taken from events.h
# /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
character_translate_table = {
'a': 0x00,
's': 0x01,
'd': 0x02,
'f': 0x03,
'h': 0x04,
'g': 0x05,
'z': 0x06,
'x': 0x07,
'c': 0x08,
'v': 0x09,
'b': 0x0b,
'q': 0x0c,
'w': 0x0d,
'e': 0x0e,
'r': 0x0f,
'y': 0x10,
't': 0x11,
'1': 0x12,
'2': 0x13,
'3': 0x14,
'4': 0x15,
'6': 0x16,
'5': 0x17,
'=': 0x18,
'9': 0x19,
'7': 0x1a,
'-': 0x1b,
'8': 0x1c,
'0': 0x1d,
']': 0x1e,
'o': 0x1f,
'u': 0x20,
'[': 0x21,
'i': 0x22,
'p': 0x23,
'l': 0x25,
'j': 0x26,
'\'': 0x27,
'k': 0x28,
';': 0x29,
'\\': 0x2a,
',': 0x2b,
'/': 0x2c,
'n': 0x2d,
'm': 0x2e,
'.': 0x2f,
'`': 0x32,
' ': 0x31,
'\r': 0x24,
'\t': 0x30,
'shift': 0x38
}
# Taken from ev_keymap.h
# http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86.1/IOHIDSystem/IOKit/hidsystem/ev_keymap.h
special_key_translate_table = {
'KEYTYPE_SOUND_UP': 0,
'KEYTYPE_SOUND_DOWN': 1,
'KEYTYPE_BRIGHTNESS_UP': 2,
'KEYTYPE_BRIGHTNESS_DOWN': 3,
'KEYTYPE_CAPS_LOCK': 4,
'KEYTYPE_HELP': 5,
'POWER_KEY': 6,
'KEYTYPE_MUTE': 7,
'UP_ARROW_KEY': 8,
'DOWN_ARROW_KEY': 9,
'KEYTYPE_NUM_LOCK': 10,
'KEYTYPE_CONTRAST_UP': 11,
'KEYTYPE_CONTRAST_DOWN': 12,
'KEYTYPE_LAUNCH_PANEL': 13,
'KEYTYPE_EJECT': 14,
'KEYTYPE_VIDMIRROR': 15,
'KEYTYPE_PLAY': 16,
'KEYTYPE_NEXT': 17,
'KEYTYPE_PREVIOUS': 18,
'KEYTYPE_FAST': 19,
'KEYTYPE_REWIND': 20,
'KEYTYPE_ILLUMINATION_UP': 21,
'KEYTYPE_ILLUMINATION_DOWN': 22,
'KEYTYPE_ILLUMINATION_TOGGLE': 23
}
class PyKeyboard(PyKeyboardMeta):
def press_key(self, key):
if key in special_key_translate_table:
self._press_special_key(key, True)
else:
self._press_normal_key(key, True)
def release_key(self, key):
if key in special_key_translate_table:
self._press_special_key(key, False)
else:
self._press_normal_key(key, False)
def special_key_assignment(self):
self.volume_mute_key = 'KEYTYPE_MUTE'
self.volume_down_key = 'KEYTYPE_SOUND_DOWN'
self.volume_up_key = 'KEYTYPE_SOUND_UP'
self.media_play_pause_key = 'KEYTYPE_PLAY'
# Doesn't work :(
# self.media_next_track_key = 'KEYTYPE_NEXT'
# self.media_prev_track_key = 'KEYTYPE_PREVIOUS'
def _press_normal_key(self, key, down):
try:
if self.is_char_shifted(key):
key_code = character_translate_table[key.lower()]
event = CGEventCreateKeyboardEvent(None,
character_translate_table['shift'], down)
CGEventPost(kCGHIDEventTap, event)
# Tiny sleep to let OS X catch up on us pressing shift
time.sleep(.01)
else:
key_code = character_translate_table[key]
event = CGEventCreateKeyboardEvent(None, key_code, down)
CGEventPost(kCGHIDEventTap, event)
except KeyError:
raise RuntimeError("Key {} not implemented.".format(key))
def _press_special_key(self, key, down):
""" Helper method for special keys.
Source: http://stackoverflow.com/questions/11045814/emulate-media-key-press-on-mac
"""
key_code = special_key_translate_table[key]
ev = NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
NSSystemDefined, # type
(0,0), # location
0xa00 if down else 0xb00, # flags
0, # timestamp
0, # window
0, # ctx
8, # subtype
(key_code << 16) | ((0xa if down else 0xb) << 8), # data1
-1 # data2
)
CGEventPost(0, ev.CGEvent())
class PyKeyboardEvent(PyKeyboardEventMeta):
def run(self):
tap = CGEventTapCreate(
kCGSessionEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventKeyDown) |
CGEventMaskBit(kCGEventKeyUp),
self.handler,
None)
loopsource = CFMachPortCreateRunLoopSource(None, tap, 0)
loop = CFRunLoopGetCurrent()
CFRunLoopAddSource(loop, loopsource, kCFRunLoopDefaultMode)
CGEventTapEnable(tap, True)
while self.state:
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5, False)
def handler(self, proxy, type, event, refcon):
key = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode)
if type == kCGEventKeyDown:
self.key_press(key)
elif type == kCGEventKeyUp:
self.key_release(key)
if self.capture:
CGEventSetType(event, kCGEventNull)
return event

View File

@ -1,14 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.

View File

@ -1,14 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.

View File

@ -1,318 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.
from ctypes import *
import win32api
from win32con import *
import pythoncom
from .base import PyKeyboardMeta, PyKeyboardEventMeta
import time
class SupportError(Exception):
"""For keys not supported on this system"""
def __init__(self, value):
self.value = value
def __str__(self):
return('The {0} key is not supported in Windows'.format(self.value))
class PyKeyboard(PyKeyboardMeta):
"""
The PyKeyboard implementation for Windows systems. This allows one to
simulate keyboard input.
"""
def __init__(self):
PyKeyboardMeta.__init__(self)
self.special_key_assignment()
def press_key(self, character=''):
"""
Press a given character key.
"""
try:
shifted = self.is_char_shifted(character)
except AttributeError:
win32api.keybd_event(character, 0, 0, 0)
else:
if shifted:
win32api.keybd_event(self.shift_key, 0, 0, 0)
char_vk = win32api.VkKeyScan(character)
win32api.keybd_event(char_vk, 0, 0, 0)
def release_key(self, character=''):
"""
Release a given character key.
"""
try:
shifted = self.is_char_shifted(character)
except AttributeError:
win32api.keybd_event(character, 0, KEYEVENTF_KEYUP, 0)
else:
if shifted:
win32api.keybd_event(self.shift_key, 0, KEYEVENTF_KEYUP, 0)
char_vk = win32api.VkKeyScan(character)
win32api.keybd_event(char_vk, 0, KEYEVENTF_KEYUP, 0)
def special_key_assignment(self):
"""
Special Key assignment for windows
"""
#As defined by Microsoft, refer to:
#http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
self.backspace_key = VK_BACK
self.tab_key = VK_TAB
self.clear_key = VK_CLEAR
self.return_key = VK_RETURN
self.enter_key = self.return_key # Because many keyboards call it "Enter"
self.shift_key = VK_SHIFT
self.shift_l_key = VK_LSHIFT
self.shift_r_key = VK_RSHIFT
self.control_key = VK_CONTROL
self.control_l_key = VK_LCONTROL
self.control_r_key = VK_RCONTROL
#Windows uses "menu" to refer to Alt...
self.menu_key = VK_MENU
self.alt_l_key = VK_LMENU
self.alt_r_key = VK_RMENU
self.alt_key = self.alt_l_key
self.pause_key = VK_PAUSE
self.caps_lock_key = VK_CAPITAL
self.capital_key = self.caps_lock_key
self.num_lock_key = VK_NUMLOCK
self.scroll_lock_key = VK_SCROLL
#Windows Language Keys,
self.kana_key = VK_KANA
self.hangeul_key = VK_HANGEUL # old name - should be here for compatibility
self.hangul_key = VK_HANGUL
self.junjua_key = VK_JUNJA
self.final_key = VK_FINAL
self.hanja_key = VK_HANJA
self.kanji_key = VK_KANJI
self.convert_key = VK_CONVERT
self.nonconvert_key = VK_NONCONVERT
self.accept_key = VK_ACCEPT
self.modechange_key = VK_MODECHANGE
#More Keys
self.escape_key = VK_ESCAPE
self.space_key = VK_SPACE
self.prior_key = VK_PRIOR
self.next_key = VK_NEXT
self.page_up_key = self.prior_key
self.page_down_key = self.next_key
self.home_key = VK_HOME
self.up_key = VK_UP
self.down_key = VK_DOWN
self.left_key = VK_LEFT
self.right_key = VK_RIGHT
self.end_key = VK_END
self.select_key = VK_SELECT
self.print_key = VK_PRINT
self.snapshot_key = VK_SNAPSHOT
self.print_screen_key = self.snapshot_key
self.execute_key = VK_EXECUTE
self.insert_key = VK_INSERT
self.delete_key = VK_DELETE
self.help_key = VK_HELP
self.windows_l_key = VK_LWIN
self.super_l_key = self.windows_l_key
self.windows_r_key = VK_RWIN
self.super_r_key = self.windows_r_key
self.apps_key = VK_APPS
#Numpad
self.keypad_keys = {'Space': None,
'Tab': None,
'Enter': None, # Needs Fixing
'F1': None,
'F2': None,
'F3': None,
'F4': None,
'Home': VK_NUMPAD7,
'Left': VK_NUMPAD4,
'Up': VK_NUMPAD8,
'Right': VK_NUMPAD6,
'Down': VK_NUMPAD2,
'Prior': None,
'Page_Up': VK_NUMPAD9,
'Next': None,
'Page_Down': VK_NUMPAD3,
'End': VK_NUMPAD1,
'Begin': None,
'Insert': VK_NUMPAD0,
'Delete': VK_DECIMAL,
'Equal': None, # Needs Fixing
'Multiply': VK_MULTIPLY,
'Add': VK_ADD,
'Separator': VK_SEPARATOR,
'Subtract': VK_SUBTRACT,
'Decimal': VK_DECIMAL,
'Divide': VK_DIVIDE,
0: VK_NUMPAD0,
1: VK_NUMPAD1,
2: VK_NUMPAD2,
3: VK_NUMPAD3,
4: VK_NUMPAD4,
5: VK_NUMPAD5,
6: VK_NUMPAD6,
7: VK_NUMPAD7,
8: VK_NUMPAD8,
9: VK_NUMPAD9}
self.numpad_keys = self.keypad_keys
#FKeys
self.function_keys = [None, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6,
VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12,
VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18,
VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24,
None, None, None, None, None, None, None, None,
None, None, None] #Up to 36 as in x11
#Miscellaneous
self.cancel_key = VK_CANCEL
self.break_key = self.cancel_key
self.mode_switch_key = VK_MODECHANGE
self.browser_back_key = VK_BROWSER_BACK
self.browser_forward_key = VK_BROWSER_FORWARD
self.processkey_key = VK_PROCESSKEY
self.attn_key = VK_ATTN
self.crsel_key = VK_CRSEL
self.exsel_key = VK_EXSEL
self.ereof_key = VK_EREOF
self.play_key = VK_PLAY
self.zoom_key = VK_ZOOM
self.noname_key = VK_NONAME
self.pa1_key = VK_PA1
self.oem_clear_key = VK_OEM_CLEAR
self.volume_mute_key = VK_VOLUME_MUTE
self.volume_down_key = VK_VOLUME_DOWN
self.volume_up_key = VK_VOLUME_UP
self.media_next_track_key = VK_MEDIA_NEXT_TRACK
self.media_prev_track_key = VK_MEDIA_PREV_TRACK
self.media_play_pause_key = VK_MEDIA_PLAY_PAUSE
self.begin_key = self.home_key
#LKeys - Unsupported
self.l_keys = [None] * 11
#RKeys - Unsupported
self.r_keys = [None] * 16
#Other unsupported Keys from X11
self.linefeed_key = None
self.find_key = None
self.meta_l_key = None
self.meta_r_key = None
self.sys_req_key = None
self.hyper_l_key = None
self.hyper_r_key = None
self.undo_key = None
self.redo_key = None
self.script_switch_key = None
class PyKeyboardEvent(PyKeyboardEventMeta):
"""
The PyKeyboardEvent implementation for Windows Systems. This allows one
to listen for keyboard input.
"""
def __init__(self):
import pyHook
PyKeyboardEventMeta.__init__(self)
self.hm = pyHook.HookManager()
self.shift_state = 0 # 0 is off, 1 is on
self.alt_state = 0 # 0 is off, 2 is on
def run(self):
"""Begin listening for keyboard input events."""
self.state = True
self.hm.KeyAll = self.handler
self.hm.HookKeyboard()
while self.state:
time.sleep(0.01)
pythoncom.PumpWaitingMessages()
def stop(self):
"""Stop listening for keyboard input events."""
self.hm.UnhookKeyboard()
self.state = False
def handler(self, reply):
"""Upper level handler of keyboard events."""
if reply.Message == pyHook.HookConstants.WM_KEYDOWN:
self._key_press(reply)
elif reply.Message == pyHook.HookConstants.WM_KEYUP:
self._key_release(reply)
elif reply.Message == pyHook.HookConstants.WM_SYSKEYDOWN:
self._key_press(reply)
elif reply.Message == pyHook.HookConstants.WM.SYSKEYUP:
self._key_release(reply)
else:
print('Keyboard event message unhandled: {0}'.format(reply.Message))
return not self.capture
def _key_press(self, event):
if self.escape_code(event): #Quit if this returns True
self.stop()
if event.GetKey() in ['Shift', 'Lshift', 'Rshift', 'Capital']:
self.toggle_shift_state()
if event.GetKey() in ['Menu', 'Lmenu', 'Rmenu']:
self.toggle_alt_state()
#print('Key Pressed!')
#print('GetKey: {0}'.format(event.GetKey())) # Name of the virtual keycode, str
#print('IsAlt: {0}'.format(event.IsAlt())) # Was the alt key depressed?, bool
#print('IsExtended: {0}'.format(event.IsExtended())) # Is this an extended key?, bool
#print('IsInjected: {0}'.format(event.IsInjected())) # Was this event generated programmatically?, bool
#print('IsTransition: {0}'.format(event.IsTransition())) #Is this a transition from up to down or vice versa?, bool
#print('ASCII: {0}'.format(event.Ascii)) # ASCII value, if one exists, str
#print('KeyID: {0}'.format(event.KeyID)) # Virtual key code, int
#print('ScanCode: {0}'.format(event.ScanCode)) # Scan code, int
def _key_release(self, event):
if event.GetKey() in ['Shift', 'Lshift', 'Rshift', 'Capital']:
self.toggle_shift_state()
if event.GetKey() in ['Menu', 'Lmenu', 'Rmenu']:
self.toggle_alt_state()
self.key_release()
#print('Key Released!')
#print('GetKey: {0}'.format(event.GetKey())) # Name of the virtual keycode, str
#print('IsAlt: {0}'.format(event.IsAlt())) # Was the alt key depressed?, bool
#print('IsExtended: {0}'.format(event.IsExtended())) # Is this an extended key?, bool
#print('IsInjected: {0}'.format(event.IsInjected())) # Was this event generated programmatically?, bool
#print('IsTransition: {0}'.format(event.IsTransition())) #Is this a transition from up to down or vice versa?, bool
#print('ASCII: {0}'.format(event.Ascii)) # ASCII value, if one exists, str
#print('KeyID: {0}'.format(event.KeyID)) # Virtual key code, int
#print('ScanCode: {0}'.format(event.ScanCode)) # Scan code, int
def escape_code(self, event):
if event.KeyID == VK_ESCAPE:
return True
return False
def toggle_shift_state(self):
'''Does toggling for the shift state.'''
if self.shift_state == 0:
self.shift_state = 1
elif self.shift_state == 1:
self.shift_state = 0
else:
return False
return True
def toggle_alt_state(self):
'''Does toggling for the alt state.'''
if self.alt_state == 0:
self.alt_state = 2
elif self.alt_state == 2:
self.alt_state = 0
else:
return False
return True

View File

@ -1,363 +0,0 @@
#Copyright 2013 Paul Barton
#
#This program 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/>.
from Xlib.display import Display
from Xlib import X
from Xlib.ext.xtest import fake_input
from Xlib.XK import string_to_keysym, keysym_to_string
from Xlib.ext import record
from Xlib.protocol import rq
from .base import PyKeyboardMeta, PyKeyboardEventMeta
import time
special_X_keysyms = {
' ': "space",
'\t': "Tab",
'\n': "Return", # for some reason this needs to be cr, not lf
'\r': "Return",
'\e': "Escape",
'!': "exclam",
'#': "numbersign",
'%': "percent",
'$': "dollar",
'&': "ampersand",
'"': "quotedbl",
'\'': "apostrophe",
'(': "parenleft",
')': "parenright",
'*': "asterisk",
'=': "equal",
'+': "plus",
',': "comma",
'-': "minus",
'.': "period",
'/': "slash",
':': "colon",
';': "semicolon",
'<': "less",
'>': "greater",
'?': "question",
'@': "at",
'[': "bracketleft",
']': "bracketright",
'\\': "backslash",
'^': "asciicircum",
'_': "underscore",
'`': "grave",
'{': "braceleft",
'|': "bar",
'}': "braceright",
'~': "asciitilde"
}
class PyKeyboard(PyKeyboardMeta):
"""
The PyKeyboard implementation for X11 systems (mostly linux). This
allows one to simulate keyboard input.
"""
def __init__(self, display=None):
PyKeyboardMeta.__init__(self)
self.display = Display(display)
self.display2 = Display(display)
self.special_key_assignment()
def press_key(self, character=''):
"""
Press a given character key. Also works with character keycodes as
integers, but not keysyms.
"""
try: # Detect uppercase or shifted character
shifted = self.is_char_shifted(character)
except AttributeError: # Handle the case of integer keycode argument
fake_input(self.display, X.KeyPress, character)
self.display.sync()
else:
if shifted:
fake_input(self.display, X.KeyPress, self.shift_key)
char_val = self.lookup_character_value(character)
fake_input(self.display, X.KeyPress, char_val)
self.display.sync()
def release_key(self, character=''):
"""
Release a given character key. Also works with character keycodes as
integers, but not keysyms.
"""
try: # Detect uppercase or shifted character
shifted = self.is_char_shifted(character)
except AttributeError: # Handle the case of integer keycode argument
fake_input(self.display, X.KeyRelease, character)
self.display.sync()
else:
if shifted:
fake_input(self.display, X.KeyRelease, self.shift_key)
char_val = self.lookup_character_value(character)
fake_input(self.display, X.KeyRelease, char_val)
self.display.sync()
def special_key_assignment(self):
"""
Determines the keycodes for common special keys on the keyboard. These
are integer values and can be passed to the other key methods.
Generally speaking, these are non-printable codes.
"""
#This set of keys compiled using the X11 keysymdef.h file as reference
#They comprise a relatively universal set of keys, though there may be
#exceptions which may come up for other OSes and vendors. Countless
#special cases exist which are not handled here, but may be extended.
#TTY Function Keys
self.backspace_key = self.lookup_character_value('BackSpace')
self.tab_key = self.lookup_character_value('Tab')
self.linefeed_key = self.lookup_character_value('Linefeed')
self.clear_key = self.lookup_character_value('Clear')
self.return_key = self.lookup_character_value('Return')
self.enter_key = self.return_key # Because many keyboards call it "Enter"
self.pause_key = self.lookup_character_value('Pause')
self.scroll_lock_key = self.lookup_character_value('Scroll_Lock')
self.sys_req_key = self.lookup_character_value('Sys_Req')
self.escape_key = self.lookup_character_value('Escape')
self.delete_key = self.lookup_character_value('Delete')
#Modifier Keys
self.shift_l_key = self.lookup_character_value('Shift_L')
self.shift_r_key = self.lookup_character_value('Shift_R')
self.shift_key = self.shift_l_key # Default Shift is left Shift
self.alt_l_key = self.lookup_character_value('Alt_L')
self.alt_r_key = self.lookup_character_value('Alt_R')
self.alt_key = self.alt_l_key # Default Alt is left Alt
self.control_l_key = self.lookup_character_value('Control_L')
self.control_r_key = self.lookup_character_value('Control_R')
self.control_key = self.control_l_key # Default Ctrl is left Ctrl
self.caps_lock_key = self.lookup_character_value('Caps_Lock')
self.capital_key = self.caps_lock_key # Some may know it as Capital
self.shift_lock_key = self.lookup_character_value('Shift_Lock')
self.meta_l_key = self.lookup_character_value('Meta_L')
self.meta_r_key = self.lookup_character_value('Meta_R')
self.super_l_key = self.lookup_character_value('Super_L')
self.windows_l_key = self.super_l_key # Cross-support; also it's printed there
self.super_r_key = self.lookup_character_value('Super_R')
self.windows_r_key = self.super_r_key # Cross-support; also it's printed there
self.hyper_l_key = self.lookup_character_value('Hyper_L')
self.hyper_r_key = self.lookup_character_value('Hyper_R')
#Cursor Control and Motion
self.home_key = self.lookup_character_value('Home')
self.up_key = self.lookup_character_value('Up')
self.down_key = self.lookup_character_value('Down')
self.left_key = self.lookup_character_value('Left')
self.right_key = self.lookup_character_value('Right')
self.end_key = self.lookup_character_value('End')
self.begin_key = self.lookup_character_value('Begin')
self.page_up_key = self.lookup_character_value('Page_Up')
self.page_down_key = self.lookup_character_value('Page_Down')
self.prior_key = self.lookup_character_value('Prior')
self.next_key = self.lookup_character_value('Next')
#Misc Functions
self.select_key = self.lookup_character_value('Select')
self.print_key = self.lookup_character_value('Print')
self.print_screen_key = self.print_key # Seems to be the same thing
self.snapshot_key = self.print_key # Another name for printscreen
self.execute_key = self.lookup_character_value('Execute')
self.insert_key = self.lookup_character_value('Insert')
self.undo_key = self.lookup_character_value('Undo')
self.redo_key = self.lookup_character_value('Redo')
self.menu_key = self.lookup_character_value('Menu')
self.apps_key = self.menu_key # Windows...
self.find_key = self.lookup_character_value('Find')
self.cancel_key = self.lookup_character_value('Cancel')
self.help_key = self.lookup_character_value('Help')
self.break_key = self.lookup_character_value('Break')
self.mode_switch_key = self.lookup_character_value('Mode_switch')
self.script_switch_key = self.lookup_character_value('script_switch')
self.num_lock_key = self.lookup_character_value('Num_Lock')
#Keypad Keys: Dictionary structure
keypad = ['Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home',
'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next',
'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal',
'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal',
'Divide', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
self.keypad_keys = {k: self.lookup_character_value('KP_'+str(k)) for k in keypad}
self.numpad_keys = self.keypad_keys
#Function Keys/ Auxilliary Keys
#FKeys
self.function_keys = [None] + [self.lookup_character_value('F'+str(i)) for i in xrange(1,36)]
#LKeys
self.l_keys = [None] + [self.lookup_character_value('L'+str(i)) for i in xrange(1,11)]
#RKeys
self.r_keys = [None] + [self.lookup_character_value('R'+str(i)) for i in xrange(1,16)]
#Unsupported keys from windows
self.kana_key = None
self.hangeul_key = None # old name - should be here for compatibility
self.hangul_key = None
self.junjua_key = None
self.final_key = None
self.hanja_key = None
self.kanji_key = None
self.convert_key = None
self.nonconvert_key = None
self.accept_key = None
self.modechange_key = None
self.sleep_key = None
def lookup_character_value(self, character):
"""
Looks up the keysym for the character then returns the keycode mapping
for that keysym.
"""
ch_keysym = string_to_keysym(character)
if ch_keysym == 0:
ch_keysym = string_to_keysym(special_X_keysyms[character])
return self.display.keysym_to_keycode(ch_keysym)
class PyKeyboardEvent(PyKeyboardEventMeta):
"""
The PyKeyboardEvent implementation for X11 systems (mostly linux). This
allows one to listen for keyboard input.
"""
def __init__(self, display=None):
PyKeyboardEventMeta.__init__(self)
self.display = Display(display)
self.display2 = Display(display)
self.ctx = self.display2.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': (X.KeyPress, X.KeyRelease),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}])
self.shift_state = 0 # 0 is off, 1 is on
self.alt_state = 0 # 0 is off, 2 is on
self.mod_keycodes = self.get_mod_keycodes()
def run(self):
"""Begin listening for keyboard input events."""
self.state = True
if self.capture:
self.display2.screen().root.grab_keyboard(True, X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime)
self.display2.record_enable_context(self.ctx, self.handler)
self.display2.record_free_context(self.ctx)
def stop(self):
"""Stop listening for keyboard input events."""
self.state = False
self.display.record_disable_context(self.ctx)
self.display.ungrab_keyboard(X.CurrentTime)
self.display.flush()
self.display2.record_disable_context(self.ctx)
self.display2.ungrab_keyboard(X.CurrentTime)
self.display2.flush()
def handler(self, reply):
"""Upper level handler of keyboard events."""
data = reply.data
while len(data):
event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None)
if event.type == X.KeyPress:
if self.escape_code(event): # Quit if this returns True
self.stop()
else:
self._key_press(event.detail)
elif event.type == X.KeyRelease:
self._key_release(event.detail)
else:
print('WTF: {0}'.format(event.type))
def _key_press(self, keycode):
"""A key has been pressed, do stuff."""
#Alter modification states
if keycode in self.mod_keycodes['Shift'] or keycode in self.mod_keycodes['Lock']:
self.toggle_shift_state()
elif keycode in self.mod_keycodes['Alt']:
self.toggle_alt_state()
else:
self.key_press(keycode)
def _key_release(self, keycode):
"""A key has been released, do stuff."""
#Alter modification states
if keycode in self.mod_keycodes['Shift']:
self.toggle_shift_state()
elif keycode in self.mod_keycodes['Alt']:
self.toggle_alt_state()
else:
self.key_release(keycode)
def escape_code(self, event):
if event.detail == self.lookup_character_value('Escape'):
return True
return False
def lookup_char_from_keycode(self, keycode):
keysym =self.display.keycode_to_keysym(keycode, self.shift_state + self.alt_state)
if keysym:
char = self.display.lookup_string(keysym)
return char
else:
return None
def get_mod_keycodes(self):
"""
Detects keycodes for modifiers and parses them into a dictionary
for easy access.
"""
modifier_mapping = self.display.get_modifier_mapping()
modifier_dict = {}
nti = [('Shift', X.ShiftMapIndex),
('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex),
('Alt', X.Mod1MapIndex), ('Mod2', X.Mod2MapIndex),
('Mod3', X.Mod3MapIndex), ('Mod4', X.Mod4MapIndex),
('Mod5', X.Mod5MapIndex), ('Lock', X.LockMapIndex)]
for n, i in nti:
modifier_dict[n] = list(modifier_mapping[i])
return modifier_dict
def lookup_character_value(self, character):
"""
Looks up the keysym for the character then returns the keycode mapping
for that keysym.
"""
ch_keysym = string_to_keysym(character)
if ch_keysym == 0:
ch_keysym = string_to_keysym(special_X_keysyms[character])
return self.display.keysym_to_keycode(ch_keysym)
def toggle_shift_state(self):
'''Does toggling for the shift state.'''
if self.shift_state == 0:
self.shift_state = 1
elif self.shift_state == 1:
self.shift_state = 0
else:
return False
return True
def toggle_alt_state(self):
'''Does toggling for the alt state.'''
if self.alt_state == 0:
self.alt_state = 2
elif self.alt_state == 2:
self.alt_state = 0
else:
return False
return True

View File

@ -11,13 +11,13 @@ import time
import requests import requests
from . import EventBus, StateMachine, EVENT_START import homeassistant as ha
from .httpinterface import HTTPInterface, SERVER_PORT import homeassistant.httpinterface as httpinterface
API_PASSWORD = "test1234" API_PASSWORD = "test1234"
HTTP_BASE_URL = "http://127.0.0.1:{}".format(SERVER_PORT) HTTP_BASE_URL = "http://127.0.0.1:{}".format(httpinterface.SERVER_PORT)
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
class TestHTTPInterface(unittest.TestCase): class TestHTTPInterface(unittest.TestCase):
@ -30,11 +30,12 @@ class TestHTTPInterface(unittest.TestCase):
if not TestHTTPInterface.HTTP_init: if not TestHTTPInterface.HTTP_init:
TestHTTPInterface.HTTP_init = True TestHTTPInterface.HTTP_init = True
HTTPInterface(self.eventbus, self.statemachine, API_PASSWORD) httpinterface.HTTPInterface(self.eventbus, self.statemachine,
API_PASSWORD)
self.statemachine.set_state("test", "INIT_STATE") self.statemachine.set_state("test", "INIT_STATE")
self.eventbus.fire(EVENT_START) self.eventbus.fire(ha.EVENT_START)
# Give objects time to startup # Give objects time to startup
time.sleep(1) time.sleep(1)
@ -42,8 +43,8 @@ class TestHTTPInterface(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): # pylint: disable=invalid-name def setUpClass(cls): # pylint: disable=invalid-name
""" things to be run when tests are started. """ """ things to be run when tests are started. """
cls.eventbus = EventBus() cls.eventbus = ha.EventBus()
cls.statemachine = StateMachine(cls.eventbus) cls.statemachine = ha.StateMachine(cls.eventbus)
def test_debug_interface(self): def test_debug_interface(self):
""" Test if we can login by comparing not logged in screen to """ Test if we can login by comparing not logged in screen to

View File

@ -1,7 +1,18 @@
""" Helper methods for various modules. """ """ Helper methods for various modules. """
from datetime import datetime
import re import re
DATE_STR_FORMAT = "%H:%M:%S %d-%m-%Y"
def sanitize_filename(filename): def sanitize_filename(filename):
""" Sanitizes a filename by removing .. / and \\. """ """ Sanitizes a filename by removing .. / and \\. """
return re.sub(r"(~|(\.\.)|/|\+)", "", filename) return re.sub(r"(~|(\.\.)|/|\+)", "", filename)
def datetime_to_str(dattim):
""" Converts datetime to a string format. """
return dattim.strftime(DATE_STR_FORMAT)
def str_to_datetime(dt_str):
""" Converts a string to a datetime object. """
return datetime.strptime(dt_str, DATE_STR_FORMAT)