Skip to content

Commit adfe253

Browse files
author
Dozen Crows
committed
Initial commit
0 parents  commit adfe253

10 files changed

+354
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.pyc
2+

Impact.ttf

133 KB
Binary file not shown.

LiberationMono-Regular.ttf

106 KB
Binary file not shown.

LiberationSansNarrow-Bold.ttf

107 KB
Binary file not shown.

LiberationSansNarrow-Regular.ttf

110 KB
Binary file not shown.

PiMony.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/python
2+
'''
3+
PiMony -- Smart remote control prototype
4+
5+
This is the main module for PiMony, a prototype smart remote control program
6+
7+
@author: Nicholas Tuckett
8+
9+
@copyright: 2014 Nicholas Tuckett. All rights reserved.
10+
11+
@license: private
12+
13+
14+
@deffield updated: Updated
15+
'''
16+
17+
import sys
18+
import os
19+
20+
from argparse import ArgumentParser
21+
from argparse import RawDescriptionHelpFormatter
22+
23+
from PyGameInterface import PyGameInterface
24+
25+
__all__ = []
26+
__version__ = 0.1
27+
__date__ = '2014-10-26'
28+
__updated__ = '2014-10-26'
29+
30+
class CLIError(Exception):
31+
'''Generic exception to raise and log different fatal errors.'''
32+
def __init__(self, msg):
33+
super(CLIError).__init__(type(self))
34+
self.msg = "E: %s" % msg
35+
def __str__(self):
36+
return self.msg
37+
def __unicode__(self):
38+
return self.msg
39+
40+
def main(argv=None): # IGNORE:C0111
41+
'''Command line options.'''
42+
43+
if argv is None:
44+
argv = sys.argv
45+
else:
46+
sys.argv.extend(argv)
47+
48+
program_name = os.path.basename(sys.argv[0])
49+
program_version = "v%s" % __version__
50+
program_build_date = str(__updated__)
51+
program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date)
52+
program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
53+
program_license = '''%s
54+
55+
Created by Nicholas Tuckett on %s.
56+
Copyright 2014 Nicholas Tuckett. All rights reserved.
57+
58+
Distributed on an "AS IS" basis without warranties
59+
or conditions of any kind, either express or implied.
60+
61+
USAGE
62+
''' % (program_shortdesc, str(__date__))
63+
64+
try:
65+
# Setup argument parser
66+
parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter)
67+
parser.add_argument("-d", "--debug", dest="debug", help="Specify debug mode: provide remote host name as value")
68+
69+
# Process arguments
70+
args = parser.parse_args()
71+
72+
if args.debug:
73+
sys.path.append(r'/home/pi/pysrc')
74+
import pydevd
75+
pydevd.settrace(args.debug)
76+
77+
interface = PyGameInterface()
78+
interface.use_framebuffer()
79+
interface.run()
80+
81+
except KeyboardInterrupt:
82+
### handle keyboard interrupt ###
83+
return 0
84+
# except Exception, e:
85+
# if DEBUG or TESTRUN:
86+
# raise(e)
87+
# indent = len(program_name) * " "
88+
# sys.stderr.write(program_name + ": " + repr(e) + "\n")
89+
# sys.stderr.write(indent + " for help use --help")
90+
# return 2
91+
92+
if __name__ == "__main__":
93+
sys.exit(main())

PyGameInterface.py

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'''
2+
Created on 25 May 2014
3+
4+
@author: ntuckett
5+
'''
6+
7+
import pygame
8+
import pygame.font
9+
import os
10+
import time
11+
import imp
12+
13+
from TouchScreenButton import TouchScreenButton
14+
import Style
15+
16+
SCREEN_WIDTH = 240
17+
SCREEN_HEIGHT = 320
18+
BUTTON_MARGIN = 1
19+
BUTTON_COLUMNS = 2
20+
BUTTON_ROWS = 6
21+
22+
BUTTON_X_OFFSET = BUTTON_MARGIN
23+
BUTTON_Y_OFFSET = BUTTON_MARGIN
24+
BUTTON_COLUMN_WIDTH = (SCREEN_WIDTH / BUTTON_COLUMNS)
25+
BUTTON_ROW_HEIGHT = (SCREEN_HEIGHT / BUTTON_ROWS)
26+
BUTTON_WIDTH = BUTTON_COLUMN_WIDTH - (2 * BUTTON_MARGIN)
27+
BUTTON_HEIGHT = BUTTON_ROW_HEIGHT - (2 * BUTTON_MARGIN)
28+
BUTTON_DOUBLE_WIDTH = (BUTTON_COLUMN_WIDTH * 2) - (2 * BUTTON_MARGIN)
29+
BUTTON_DOUBLE_HEIGHT = (BUTTON_ROW_HEIGHT * 2) - (2 * BUTTON_MARGIN)
30+
31+
class PyGameInterface(object):
32+
def __init__(self):
33+
pygame.font.init()
34+
#self.font = pygame.font.Font("LiberationSansNarrow-Bold.ttf", 14)
35+
#self.font_large = pygame.font.Font("LiberationSansNarrow-Bold.ttf", 16)
36+
self.font = pygame.font.Font("Impact.ttf", 16)
37+
self.font_large = pygame.font.Font("Impact.ttf", 18)
38+
39+
def use_window(self):
40+
os.environ['SDL_VIDEODRIVER'] = 'x11'
41+
pygame.display.init()
42+
self.screen = pygame.display.set_mode((240,320))
43+
44+
def use_framebuffer(self):
45+
os.environ['SDL_VIDEODRIVER'] = 'fbcon'
46+
os.environ["SDL_FBDEV"] = "/dev/fb1"
47+
os.environ["SDL_MOUSEDEV"] = "/dev/input/touchscreen"
48+
os.environ["SDL_MOUSEDRV"] = "TSLIB"
49+
50+
pygame.display.init()
51+
self.screen = pygame.display.set_mode((240,320), pygame.FULLSCREEN, 16)
52+
pygame.mouse.set_visible(False)
53+
54+
def run(self):
55+
running = True
56+
57+
self.button_style = { Style.FONT: self.font, Style.BACKGROUND_COLOUR: (255,128,0), Style.BORDER_COLOUR: (255, 255, 255),
58+
Style.BORDER_WIDTH: 1, Style.TEXT_COLOUR: (255, 255, 255), Style.HIGHLIGHT_COLOUR: (0, 255, 0) }
59+
self.button_style_large = { Style.FONT: self.font_large, Style.BACKGROUND_COLOUR: (255,128,0), Style.BORDER_COLOUR: (255, 255, 255),
60+
Style.BORDER_WIDTH: 1, Style.TEXT_COLOUR: (255, 255, 255), Style.HIGHLIGHT_COLOUR: (0, 255, 0) }
61+
62+
self.buttons = [TouchScreenButton(x * BUTTON_COLUMN_WIDTH + BUTTON_X_OFFSET,
63+
y * BUTTON_ROW_HEIGHT + BUTTON_Y_OFFSET,
64+
BUTTON_WIDTH,
65+
BUTTON_HEIGHT,
66+
"Button %d-%d" % (x, y),
67+
"B%d%d" % (x, y),
68+
self.button_style
69+
) for y in range(0, 2) for x in range(0, BUTTON_COLUMNS) ]
70+
71+
self.buttons.extend([TouchScreenButton(x * BUTTON_COLUMN_WIDTH + BUTTON_X_OFFSET,
72+
y * BUTTON_ROW_HEIGHT + BUTTON_Y_OFFSET,
73+
BUTTON_DOUBLE_WIDTH,
74+
BUTTON_HEIGHT,
75+
"Button %d-%d" % (x, y),
76+
"B%d%d" % (x, y),
77+
self.button_style_large
78+
) for y in range(2, 4) for x in range(0, BUTTON_COLUMNS, BUTTON_COLUMNS) ])
79+
80+
self.buttons.extend([TouchScreenButton(x * BUTTON_COLUMN_WIDTH + BUTTON_X_OFFSET,
81+
y * BUTTON_ROW_HEIGHT + BUTTON_Y_OFFSET,
82+
BUTTON_WIDTH,
83+
BUTTON_DOUBLE_HEIGHT,
84+
"Button %d-%d" % (x, y),
85+
"B%d%d" % (x, y),
86+
self.button_style
87+
) for y in range(4, 6, 2) for x in range(0, BUTTON_COLUMNS) ])
88+
89+
self.screen.fill((0, 0, 0))
90+
for button in self.buttons:
91+
button.render(self.screen)
92+
93+
self.display_dirty = True
94+
self.current_button = None
95+
96+
while running:
97+
if self.display_dirty:
98+
pygame.display.flip()
99+
self.display_dirty = False
100+
101+
event = pygame.event.wait()
102+
while event.type != pygame.NOEVENT:
103+
if event.type == pygame.MOUSEBUTTONDOWN:
104+
print event
105+
for button in self.buttons:
106+
if button.hit_test(event.pos):
107+
self.current_button = button
108+
button.highlight(True)
109+
self.display_dirty = True
110+
self.current_button.render(self.screen)
111+
break
112+
113+
if event.type == pygame.MOUSEMOTION and self.current_button:
114+
self.current_button.highlight(self.current_button.hit_test(event.pos))
115+
self.display_dirty = True
116+
self.current_button.render(self.screen)
117+
118+
if event.type == pygame.MOUSEBUTTONUP and self.current_button:
119+
self.current_button.highlight(False)
120+
self.display_dirty = True
121+
self.current_button.render(self.screen)
122+
self.current_button = None
123+
124+
if event.type == pygame.QUIT:
125+
running = False
126+
127+
event = pygame.event.poll()
128+
129+

Style.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
BACKGROUND_COLOUR = "background-colour"
2+
BORDER_COLOUR = "border-colour"
3+
BORDER_WIDTH = "border-width"
4+
FONT = "font"
5+
TEXT_COLOUR = "text-colour"
6+
HIGHLIGHT_COLOUR = "highlight-colour"

TouchScreenButton.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#
2+
# Style data:
3+
# text_colour
4+
# background_colour
5+
# border_colour
6+
# highlight_colour
7+
# font
8+
9+
import pygame.draw
10+
11+
import Style
12+
13+
class TouchScreenButton(object):
14+
def __init__(self, x, y, width, height, prompt, code, style):
15+
self.x = x
16+
self.y = y
17+
self.width = width
18+
self.height = height
19+
self.prompt = prompt
20+
self.code = code
21+
self.style = style
22+
self.rect = (self.x, self.y, self.width, self.height)
23+
self.highlighted = False
24+
self.text_surface = self.style[Style.FONT].render(self.prompt, False, self.style[Style.TEXT_COLOUR])
25+
26+
def render(self, surface):
27+
if self.highlighted:
28+
surface.fill(self.style[Style.HIGHLIGHT_COLOUR], self.rect)
29+
else:
30+
surface.fill(self.style[Style.BACKGROUND_COLOUR], self.rect)
31+
32+
pygame.draw.rect(surface, self.style[Style.BORDER_COLOUR], self.rect, self.style[Style.BORDER_WIDTH])
33+
text_width, text_height = self.text_surface.get_size()
34+
surface.blit(self.text_surface, (self.x + (self.width - text_width) / 2, self.y + (self.height - text_height) / 2))
35+
36+
def highlight(self, highlight):
37+
self.highlighted = highlight
38+
39+
def hit_test(self, point):
40+
relative_x = point[0] - self.x
41+
if relative_x >= 0 and relative_x < self.width:
42+
relative_y = point[1] - self.y
43+
return relative_y >= 0 and relative_y < self.height
44+
return False

plan.txt

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
Phase 1 - first working buttons
2+
===============================
3+
- Create demo set of touch buttons to control TV:
4+
Power, Volume up/down, Channel up/down, Guide, Back, Input
5+
- Associate each with an LIRC string
6+
e.g. Power: RM-ED050-12 KEY_POWER Phillips-HTS KEY_POWER
7+
Volume: Phillips-HTS KEY_VOLUMEUP
8+
Phillips-HTS KEY_VOLUMEDOWN
9+
- When a button is pressed, spawn command with string
10+
e.g. "irsend SEND_ONCE %s" % button_lirc_string
11+
- encapsulate irsend it in its own class/module to replace later
12+
13+
Phase 2 - GPIO buttons
14+
======================
15+
Ideas:
16+
* Use RPIO in Python to set up interrupts/polling on buttons
17+
- modify startup to ensure Python RPIO module can be used without sudo
18+
- create GPIO button class to associate GPIO pin and LIRC string
19+
- on press 'edge' spawn irsend as above
20+
* Use PikeyD
21+
- Set up PikeyD to generate key presses from GPIO pin buttons
22+
- Add Keyboard button class to associate keypress and LIRC string
23+
- on keypress, spawn irsend as above
24+
25+
Phase 3 - Switch matrix
26+
=======================
27+
Ideas:
28+
* Use SMBus in Python to poll I2C
29+
- modify startup to ensure Python SMBus module can be used without sudo
30+
- create SwitchMatrix button class to associate matrix code and LIRC string
31+
- poll I2C to read matrix; on press 'edge' decode to LIRC string and spawn irsend as above
32+
* Use PikeyD
33+
- Modify PikeyD to poll, read and decode matrix via I2C, and generate keypress
34+
- Add Keyboard button class to associate keypress and LIRC string
35+
- on keypress, spawn irsend as above
36+
37+
Phase 4 - Direct LIRC connection
38+
================================
39+
Replace spawn of irsend with direct LIRC communication via directly opening & using
40+
socket in PyMony code.
41+
42+
Phase 5 - Devices
43+
=================
44+
* Device has:
45+
- Name
46+
- State variables: power (on/off), input (1,2,3...), audio mode (1,2,3...)
47+
- Current state vector: power, input, audio mode
48+
- Button layout: map touch screen, gpio, keys to remote codes, and optionally to state variables
49+
* Provide device handling
50+
- Device initialisation: read definitions, create instances & default state variables
51+
- Device selection
52+
- Device layout rendering
53+
- Device layout processing (read input, generate remote codes and state changes)
54+
55+
Phase 6 - Activities
56+
====================
57+
* Activity has:
58+
- Name
59+
- Expected state for each available device
60+
* Activity processing
61+
- Initialisation: read definitions, create instances
62+
- Activity selection and switching
63+
- Process expected device state against known state; issue IR commands to achieve expected state
64+
65+
Phase 7 - Advanced UI
66+
=====================
67+
* Richer styles:
68+
- Varied colours
69+
- Varied fonts: faces, styles, colours
70+
- Bitmaps
71+
* More flexible layouts:
72+
- Variable size grids
73+
- Crosses and stars
74+
- Circular
75+
76+
Phase 8 - C/C++ Implementation
77+
==============================
78+
79+
Phase 9 - Microcontroller Implementation
80+
========================================

0 commit comments

Comments
 (0)