Skip to content
This repository has been archived by the owner on Jul 15, 2020. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
colinodell committed Jul 2, 2017
0 parents commit aaf51b8
Show file tree
Hide file tree
Showing 51 changed files with 503 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .env.dist
@@ -0,0 +1,4 @@
PINS=1234,5678
MQTT_HOST=192.168.1.123
MQTT_USER=username
MQTT_PASS=correcthorsebatterystaple
10 changes: 10 additions & 0 deletions .gitignore
@@ -0,0 +1,10 @@
# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# IDE
.idea/
21 changes: 21 additions & 0 deletions LICENSE
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 Colin O'Dell <colinodell@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
22 changes: 22 additions & 0 deletions README.md
@@ -0,0 +1,22 @@
# mqtt-control-panel

A simple alarm control panel for Home Assistant's `manual_mqtt` alarm. Designed to run on a Raspberry Pi using an Adafruit 3.5" PiTFT.

![](screenshot.png)

# Hardware

- Raspberry Pi Zero Wireless (other modern Pis will likely work fine)
- Adafruit PiTFT 3.5" display

# Requirements

This project requires Python 2.7 with the following packages:

- paho-mqtt
- pygame
- python-dotenv

# Configuration

Copy `.env.dist` to `.env` and update the values accordingly.
3 changes: 3 additions & 0 deletions alarmpanel/__init__.py
@@ -0,0 +1,3 @@
from ui import UI
from button import Button
from status import StatusLine
79 changes: 79 additions & 0 deletions alarmpanel/button.py
@@ -0,0 +1,79 @@
# Button is a simple tappable screen region. Each has:
# - bounding rect ((X,Y,W,H) in pixels)
# - a state
# - image bitmap
# - optional single callback function
# - optional single value passed to callback

# Default state for all buttons
STATE_DEFAULT = 0
# State when the button is currently pressed
STATE_PRESSED = 1
# Action buttons: action is available (proper pin has been entered)
STATE_AVAILABLE = 2
# Action buttons: used to indicate which state the alarm is in
STATE_ACTIVE = 3
# The following states are for the pin input indicator
STATE_1 = 4
STATE_2 = 5
STATE_3 = 6
STATE_4_GOOD = 7
STATE_4_BAD = 8


class Button(object):
def __init__(self, ui, rect, **kwargs):
self.ui = ui
self.rect = rect # Bounds
self.imageFiles = {}
self.bitmaps = {} # Lazy-loaded
self.callback = None # Callback function
self.value = None # Value passed to callback
self.state = 0
for key, value in kwargs.iteritems():
if key == 'imageFiles':
self.imageFiles = value
elif key == 'cb':
self.callback = value
elif key == 'value':
self.value = value

def set_state(self, state):
if state != self.state:
self.state = state
self.draw()

def down(self, pos):
if self.selected(pos):
if self.state == STATE_DEFAULT and self.bitmaps.has_key(STATE_PRESSED):
self.set_state(STATE_PRESSED)

if self.callback:
if self.value is None:
self.callback()
else:
self.callback(self.value)

return True

def up(self, pos):
if self.selected(pos) and self.state == STATE_PRESSED:
self.set_state(STATE_DEFAULT)
return True

def selected(self, pos):
x1 = self.rect[0]
y1 = self.rect[1]
x2 = x1 + self.rect[2] - 1
y2 = y1 + self.rect[3] - 1
if ((pos[0] >= x1) and (pos[0] <= x2) and
(pos[1] >= y1) and (pos[1] <= y2)):
return True
return False

def draw(self):
if self.bitmaps:
bitmap = self.bitmaps[self.state] if self.state in self.bitmaps else self.bitmaps[0]
self.ui.blit(bitmap,
(self.rect[0] + (self.rect[2] - bitmap.get_width()) / 2,
self.rect[1] + (self.rect[3] - bitmap.get_height()) / 2))
9 changes: 9 additions & 0 deletions alarmpanel/image.py
@@ -0,0 +1,9 @@
import pygame

class Image:
def __init__(self, path, name):
self.name = name
try:
self.bitmap = pygame.image.load(path + '/' + name + '.png')
except:
pass
13 changes: 13 additions & 0 deletions alarmpanel/status.py
@@ -0,0 +1,13 @@
class StatusLine:
def __init__(self, ui, rect, color):
self.ui = ui
self.rect = rect
self.color = color
self.message = ''

def set(self, message):
if self.message != message:
self.message = message

# Draw the label
self.ui.draw_text(self.message, self.rect, self.color)
111 changes: 111 additions & 0 deletions alarmpanel/ui.py
@@ -0,0 +1,111 @@
import os

import pygame

from alarmpanel.button import STATE_DEFAULT

# A simple UI which only redraws parts of the screen as needed (faster than redrawing the whole screen all the time)
class UI:
def __init__(self, background = None):
# Init framebuffer/touchscreen environment variables
os.putenv('SDL_VIDEODRIVER', 'fbcon')
os.putenv('SDL_FBDEV', '/dev/fb1')
os.putenv('SDL_MOUSEDRV', 'TSLIB')
os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen')

# Init pygame and screen
print "Initting..."
pygame.init()
print "Setting Mouse invisible..."
pygame.mouse.set_visible(False)
print "Setting fullscreen..."
modes = pygame.display.list_modes(16)
self._screen = pygame.display.set_mode(modes[0], pygame.FULLSCREEN, 16)
self._needs_update = True

# Load background
self._background = pygame.image.load(background)
self._screen.fill(0)
self._screen.blit(self._background, (0, 0))
pygame.display.update()

# Load font
self._font = pygame.font.SysFont("Arial", 24)

self._images = []
self._buttons = []

self.update()

def blit(self, *args, **kwargs):
self._screen.blit(*args, **kwargs)
self.schedule_update()

def blit_background(self, rect):
self._screen.blit(self._background, rect, rect)
self.schedule_update()

def load_images(self, path):
import fnmatch
from alarmpanel.image import Image

for file in os.listdir(path):
if fnmatch.fnmatch(file, '*.png'):
name = file.split('.')[0]
self._images.append(Image(path, name))

def create_button(self, rect, **kwargs):
from alarmpanel import Button

button = Button(self, rect, **kwargs)

for buttonState, imageFile in button.imageFiles.iteritems(): # For each image name defined on the button...
for image in self._images: # For each icon...
if imageFile == image.name: # Compare names; match?
button.bitmaps[buttonState] = image.bitmap # Assign Icon to Button
button.imageFiles[buttonState] = None # Name no longer used; allow garbage collection

button.draw()

self._buttons.append(button)

return button

def create_status_line(self, rect, color=(255, 255, 255)):
from alarmpanel import StatusLine
return StatusLine(self, pygame.Rect(rect), color)

def draw_text(self, message, rect, color):

# Redraw the background area behind the text
self.blit_background(rect)

# Draw the label
label = self._font.render(message, 1, color)
self._screen.blit(label, rect)

self.schedule_update()

def schedule_update(self):
self._needs_update = True

def update(self):
if self._needs_update:
self._needs_update = False
pygame.display.update()

def process_input(self):
# Process touchscreen input
from alarmpanel.button import STATE_PRESSED
for event in pygame.event.get():
if event.type is pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
for b in self._buttons:
if b.down(pos): break
elif event.type is pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
for b in self._buttons:
if b.up(pos): pass
# Redraw other buttons which might be stuck in the down position
elif b.state == STATE_PRESSED:
b.set_state(STATE_DEFAULT)
Binary file added images/0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/0_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/1_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/2_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/3_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/4.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/4_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/5.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/5_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/6.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/6_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/7.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/7_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/8.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/8_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/9.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/9_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/arm_away_active.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/arm_away_available.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/arm_away_inactive.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/arm_home_active.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/arm_home_available.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/arm_home_inactive.png
Binary file added images/asterisk.png
Binary file added images/asterisk_pressed.png
Binary file added images/bg.png
Binary file added images/disarm_active.png
Binary file added images/disarm_available.png
Binary file added images/disarm_inactive.png
Binary file added images/input_1.png
Binary file added images/input_2.png
Binary file added images/input_3.png
Binary file added images/input_4_bad.png
Binary file added images/input_4_good.png
Binary file added images/input_empty.png
Binary file added images/pound.png
Binary file added images/pound_pressed.png

0 comments on commit aaf51b8

Please sign in to comment.