Skip to content

Commit

Permalink
Merge d61a80e into 4e4c80c
Browse files Browse the repository at this point in the history
  • Loading branch information
digitaltom committed Jul 9, 2018
2 parents 4e4c80c + d61a80e commit 6e2419e
Show file tree
Hide file tree
Showing 44 changed files with 173 additions and 83 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
*.rbc
capybara-*.html
.rspec
/log
/tmp
/db/*.sqlite3
Expand Down
3 changes: 3 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--require spec_helper
--format documentation
--color
3 changes: 3 additions & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
2.3.3
# ruby version 2.3.3 from raspbian stretch
# to compile it on a more recent linux see: https://github.com/rbenv/ruby-build/wiki#openssl-usrincludeopensslasn1_mach102-error-error-this-file-is-obsolete-please-update-your-software
# on openSUSE:
# sudo zypper rm libopenssl-1_1-devel
# sudo zypper in libopenssl-1_0_0-devel
30 changes: 29 additions & 1 deletion INSTALL-RASPBIAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,32 @@
do the setup *headless*: https://hackernoon.com/raspberry-pi-headless-install-462ccabd75d0
- Use `sudo raspi-config` to set the password and timezone
- Setup Wifi: https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md
- Install needed packages: `sudo apt-get install vim ruby ruby-dev git imagemagick time sqlite3 gphoto2 libssl-dev nodejs libsqlite3-dev`
- Install needed packages: `sudo apt-get install vim ruby ruby-dev git imagemagick time sqlite3 gphoto2 libssl-dev nodejs libsqlite3-dev network-manager`


## Wifi Setup on Raspbian Stretch

### With Networkmanager

[Fedora docs](https://docs-old.fedoraproject.org/en-US/Fedora/23/html/Networking_Guide/sec-Connecting_to_a_Network_Using_nmcli.html) have a pretty good description how to handle connections with [Networkmanager](https://raspberrypi.stackexchange.com/questions/29783/how-to-setup-network-manager-on-raspbian) on the console.

To connect the raspi to an wifi network (either provided by an on site router, or an access point running on your tablet), I ran:

```
> nmcli con add con-name nexus7-photobox ifname wlan0 type wifi ssid photobox
> nmcli con modify nexus7-photobox wifi-sec.key-mgmt wpa-psk
> nmcli con modify nexus7-photobox wifi-sec.psk '<your password>'
> nmcli con up nexus7-photobox
```

`nmcli dev status` will display the current connection state.

If you configure multiple connections, they can have assigned a priority in case multiple of them are available:

```
nmcli -f autoconnect-priority,name c
nmcli c mod "mypreferred" conn.autoconnect-p 10
nmcli c mod "xfinitywifi" conn.autoconnect-p -10
```

The available connection are stored at `/etc/Networkmanager/system-connections`. Don't forget to restart Networkmanager (`systemctl restart NetworkManager`) after changging files manually.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ The Photobooth can run on any Linux server, for building a portable photo booth

General instructions on how to install Rasbian on the Raspberry can be found in [INSTALL-RASPBIAN.md](INSTALL-RASPBIAN.md)

## Network Setup

You basically have 3 options how to connect your Tablet to your Raspberry Pi server:

- Both are connected to the same wifi network. (Setup for [Raspbian Stretch](https://github.com/digitaltom/photobooth/blob/master/INSTALL-RASPBIAN.md), openSUSE)
- Your raspi acts as an access point for the tablet (Setup for [Raspbian Stretch](https://www.raspberrypi.org/documentation/configuration/wireless/access-point.md), openSUSE)
- Your tablet acts as an access point for the raspi. (Manuals for [Iphone](https://support.apple.com/de-de/ht204023) and [Android](https://www.dasheimnetzwerk.de/einrichten/Einrichten_OS_Androidx/Kapitel_Androidx_WLAN_AP.html))

It can be tricky to find out the IP address of you raspi. An [task](https://github.com/digitaltom/photobooth/issues/21) to improve this is created.
From your notebook you can use `sudo nmap -sP 192.168.178.1/24` to discover active devices in your network.

## Software Setup

- Clone the photobooth repo:
Expand Down
43 changes: 17 additions & 26 deletions app/assets/javascripts/angular/controllers/main_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,25 @@ photoBoothApp.controller('MainCtrl', [
'$interval',
function ($scope, $http, $uibModal, $log, $rootScope, $timeout, $interval) {

$scope.shoot_step = function (counter, delay) {
$timeout(function() {
$rootScope.current_shoot = counter;
$rootScope.shoot_progress = 25*counter;
$rootScope.shoot_progress_txt = counter + ' / 4';
}, delay);
}

$scope.takePicture = function () {

$rootScope.current_shoot = 0;
$rootScope.shoot_txt = 'Take pose!';
$rootScope.shoot_progress_txt = '';
$scope.openShootModal();

var countdown_delay = 3000;
// ms before shooting
var countdown_delay = 2000;
// ms for each camera picture
var picture_delay = 4000;

$rootScope.shoot_progress = 100;
countdown = $interval(function() {
Expand All @@ -40,34 +51,15 @@ photoBoothApp.controller('MainCtrl', [
);
}, countdown_delay);

$scope.shoot_step(1, picture_delay * 1 + countdown_delay);
$scope.shoot_step(2, picture_delay * 2 + countdown_delay);
$scope.shoot_step(3, picture_delay * 3 + countdown_delay);
$scope.shoot_step(4, picture_delay * 4 + countdown_delay);

// This fakes the progress in the modal. Change timeouts for
// different camera settings

$timeout(function() {
$rootScope.current_shoot = 1;
$rootScope.shoot_progress = 25;
$rootScope.shoot_progress_txt = '1 / 4';
}, 2000 + countdown_delay);
$timeout(function() {
$rootScope.current_shoot = 2;
$rootScope.shoot_progress = 50;
$rootScope.shoot_progress_txt = '2 / 4';
}, 4000 + countdown_delay);
$timeout(function() {
$rootScope.current_shoot = 3;
$rootScope.shoot_progress = 75;
$rootScope.shoot_progress_txt = '3 / 4';
}, 7000 + countdown_delay);
$timeout(function() {
$rootScope.current_shoot = 4;
$rootScope.shoot_progress = 100;
$rootScope.shoot_progress_txt = '4 / 4';
}, 10000 + countdown_delay);
$timeout(function() {
$rootScope.current_shoot = 5;
$rootScope.shoot_txt = 'Processing animation';
}, 13000 + countdown_delay);
}, picture_delay * 5 + countdown_delay);

};

Expand Down Expand Up @@ -99,4 +91,3 @@ photoBoothApp.controller('MainCtrl', [
$scope.rw = (location.search != '?rw/');

}]);

2 changes: 1 addition & 1 deletion app/assets/stylesheets/photobooth.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ body {
max-width: 95%;
}

.shoot_button:hover {
.shoot_button:hover, .shoot_button:active {
background-color: #fff;
}

Expand Down
8 changes: 5 additions & 3 deletions app/mailers/picture_set_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
class PictureSetMailer < ApplicationMailer

def image_email(email, picture_set)
attachments.inline['animation.gif'] = File.read(File.join(picture_set.dir, picture_set.animation))
@picture_set = picture_set
attachments[picture_set.animation] = File.read(File.join(picture_set.dir, picture_set.animation))
if File.exist?(File.join(picture_set.dir, picture_set.combined))
attachments.inline['combined.jpg'] = File.read(File.join(picture_set.dir, picture_set.combined))
attachments[picture_set.combined] = File.read(File.join(picture_set.dir, picture_set.combined))
end
I18n.with_locale(OPTS.locale) do
mail(to: email, subject: I18n.t('picture_set_mailer.image_email.subject', name: OPTS.image_caption))
mail(to: "#{email} <#{email}>",
subject: I18n.t('picture_set_mailer.image_email.subject', name: OPTS.image_caption))
end
end
end
63 changes: 28 additions & 35 deletions app/models/picture_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def create(date: Time.now.getlocal.strftime(DATE_FORMAT))
# wait until convert jobs are finished
until jobs.none?(&:status) do end
picture_set.create_animation
picture_set.combine_images if OPTS.render_collage
background_thread { picture_set.combine_images(overwrite: true) } if OPTS.render_collage
(1..4).each { |i| GpioPort.off(GpioPort::GPIO_PORTS["PICTURE#{i}"]) }
GpioPort.off(GpioPort::GPIO_PORTS['PROCESSING'])
picture_set
Expand All @@ -50,25 +50,18 @@ def capture_job(num, picture_set, angle)
GpioPort.on(GpioPort::GPIO_PORTS["PICTURE#{num}"])
begin
retries ||= 0
Syscall.execute("gphoto2 --capture-image-and-download --filename #{picture_set.date}_#{num}.jpg", dir: picture_set.dir)
Syscall.execute('gphoto2 --capture-image-and-download --no-keep' \
"--filename #{picture_set.date}_#{num}.jpg", dir: picture_set.dir)
raise 'Image capture failed' unless File.exist?(File.join(picture_set.dir, "#{picture_set.date}_#{num}.jpg"))
rescue StandardError => e
# rubocop:disable GuardClause
if (retries += 1) < 3
Rails.logger.warn("Retrying image ##{num} capture...")
retry
else
raise e
end
# rubocop:enable GuardClause
Rails.logger.warn("Retrying image ##{num} capture (#{retries})...") && retry if (retries += 1) < 3
raise e
end
convert_thread(num, picture_set, angle)
background_thread { picture_set.convert_to_polaroid(num, angle) }
end

def convert_thread(num, picture_set, angle)
t = Thread.new do
picture_set.convert_to_polaroid(num, angle)
end
def background_thread
t = Thread.new { yield }
t.abort_on_exception = true
t
end
Expand All @@ -90,41 +83,41 @@ def destroy

def convert_to_polaroid(num, angle)
caption = OPTS.image_caption || date
Syscall.execute("time convert -caption '#{caption}' #{date}_#{num}.jpg " \
'-scale 600 ' \
'-bordercolor Snow ' \
'-density 100 ' \
'-gravity center ' \
"-pointsize #{OPTS.image_fontsize} " \
"-polaroid -#{angle} " \
'-trim +repage ' \
"#{date}_#{num}#{POLAROID_SUFFIX}", dir: dir)
Syscall.execute("convert -caption '#{caption}' #{date}_#{num}.jpg " \
'-scale 600 ' \
'-bordercolor Snow ' \
'-density 100 ' \
'-gravity center ' \
"-pointsize #{OPTS.image_fontsize} " \
"-polaroid -#{angle} " \
'-trim +repage ' \
"#{date}_#{num}#{POLAROID_SUFFIX}", dir: dir, timing: true)
end

# Merge all polaroid previews to an animated gif
def create_animation(overwrite: false)
if File.exist?(File.join(dir, animation)) && !overwrite
if !overwrite && File.exist?(File.join(dir, animation))
Rails.logger.info "Skipping for existing animation #{dir}"
else
Rails.logger.info "Creating animation for #{dir}"
Syscall.execute("time convert -delay 60 #{date}_[1-4]#{POLAROID_SUFFIX} #{animation}", dir: dir)
Syscall.execute("convert -delay 60 #{date}_[1-4]#{POLAROID_SUFFIX} #{animation}", dir: dir, timing: true)
end
end

# Create collage of all images
def combine_images(overwrite: false)
if File.exist?(File.join(dir, combined)) && !overwrite
if !overwrite && File.exist?(File.join(dir, combined))
Rails.logger.info "Skipping for collage creation for #{dir}"
else
Rails.logger.info "Creating collage for #{dir}"
Syscall.execute("time montage -geometry '25%x25%+25+25<' " \
"-background '#{OPTS.background_color}' " \
"-title '#{OPTS.image_caption}' " \
"-font '#{OPTS.font}' " \
"-fill '#{OPTS.font_color}' " \
"-pointsize #{OPTS.combined_image_fontsize} " \
"-gravity 'Center' #{date}_[1-4].jpg " \
"#{date}#{COMBINED_SUFFIX}", dir: dir)
Syscall.execute("montage -geometry '25%x25%+25+25<' " \
"-background '#{OPTS.background_color}' " \
"-title '#{OPTS.image_caption}' " \
"-font '#{OPTS.font}' " \
"-fill '#{OPTS.font_color}' " \
"-pointsize #{OPTS.combined_image_fontsize} " \
"-gravity 'Center' #{date}_[1-4].jpg " \
"#{date}#{COMBINED_SUFFIX}", dir: dir, timing: true)
end
end

Expand Down
4 changes: 2 additions & 2 deletions app/models/syscall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

class Syscall

def self.execute(cmd, dir: Rails.root)
def self.execute(cmd, timing: false, dir: Rails.root)
output = ''
Rails.logger.debug("Executing: #{cmd}")
cmd = "export LC_ALL=C; #{cmd}"
cmd = "export LC_ALL=C; #{'time' if timing} #{cmd}"
Open3.popen2e(cmd, chdir: dir) do |_, stderr, wait_thr|
output = stderr.read
Rails.logger.debug("Stderr: #{output}")
Expand Down
7 changes: 3 additions & 4 deletions app/views/picture_set_mailer/image_email.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
%p
= t('.greeting')
%p
= image_tag attachments['animation.gif'].url
- if attachments.inline['combined.jpg']
%p
= image_tag attachments['combined.jpg'].url
= '--'
%p
= link_to 'github.com/digitaltom/photobooth/', 'https://github.com/digitaltom/photobooth/'
6 changes: 6 additions & 0 deletions app/views/picture_set_mailer/image_email.text.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
= t('.header', name: OPTS.image_caption)
= t('.content')
= t('.greeting')
= ''
= '--'
https://github.com/digitaltom/photobooth/
1 change: 1 addition & 0 deletions config/options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ default:
test:

send_by_email: true
render_collage: false
2 changes: 1 addition & 1 deletion config/unicorn.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

worker_processes 3
timeout 60
timeout 120
preload_app true

# listen '0.0.0.0', tcp_nopush: true
2 changes: 1 addition & 1 deletion spec/features/index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
end

it 'shows picture sets' do
expect(page).to have_css('img.gallery-img')
expect(page).to have_selector('img.gallery-img')
end
end

Expand Down
8 changes: 7 additions & 1 deletion spec/features/take_a_picture_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@

feature 'Photobox take picture', js: true do

it 'shows countdown modal' do
before do
visit '/?rw/#/'
end

it 'shows countdown modal' do
expect(PictureSet).to receive(:create)

click_button('take a picture')
expect(page).to have_content 'Take pose!'
sleep(3)
end

end
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions spec/models/gpio_port_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
GpioPort.on(5)
end

it 'continues if unsetting pin fails' do
pin = double
expect(File).to receive(:open).with('/sys/class/gpio/unexport', 'w').and_raise(Errno::EINVAL)
expect(PiPiper::Pin).to receive(:new).with(pin: 5, direction: :out).and_return(pin)
expect(pin).to receive(:on)
GpioPort.on(5)
end

end

describe '#off' do
Expand Down

0 comments on commit 6e2419e

Please sign in to comment.