Skip to content

Commit

Permalink
Merge pull request #149 from collectiveidea/dg/pi
Browse files Browse the repository at this point in the history
Rough but working start to running on the pi
  • Loading branch information
danielmorrison authored Oct 29, 2024
2 parents bed41ea + 810bc79 commit b3a54f7
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pi/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source "https://rubygems.org"
ruby "3.2.1"

gem "action_cable_client"
gem "json"
gem "sd_notify"
29 changes: 29 additions & 0 deletions pi/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
GEM
remote: https://rubygems.org/
specs:
action_cable_client (3.1.0)
websocket-eventmachine-client (>= 1.2.0)
eventmachine (1.2.7)
json (2.6.3)
websocket (1.2.9)
websocket-eventmachine-base (1.2.0)
eventmachine (~> 1.0)
websocket (~> 1.0)
websocket-native (~> 1.0)
websocket-eventmachine-client (1.3.0)
websocket-eventmachine-base (~> 1.0)
websocket-native (1.0.0)

PLATFORMS
arm64-darwin-21
armv7l-linux-eabihf

DEPENDENCIES
action_cable_client
json

RUBY VERSION
ruby 3.2.1p31

BUNDLED WITH
2.4.7
30 changes: 30 additions & 0 deletions pi/buildlight_listener.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[Unit]
Description=Buildlight Listener
After=network.target

[Service]
Type=notify

# If theprocess locks up, systemd's watchdog will restart it within seconds.
WatchdogSec=60

# Preferably configure a non-privileged user
User=pi

# The path to your application code root directory.
# Also replace the "<YOUR_APP_PATH>" placeholders below with this path.
# Example /home/username/myapp
WorkingDirectory=/home/pi/listener

# Helpful for debugging socket activation, etc.
# Environment=PUMA_DEBUG=1

# SystemD will not run puma even if it is in your path. You must specify
# an absolute URL to puma. For example /usr/local/bin/puma
# Alternatively, create a binstub with `bundle binstubs puma --path ./sbin` in the WorkingDirectory
ExecStart=/home/pi/listener/listener.rb

Restart=always

[Install]
WantedBy=multi-user.target
131 changes: 131 additions & 0 deletions pi/listener.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/local/bin/ruby

require "bundler/setup"
require "json"
require "action_cable_client"
require "sd_notify"

if ENV["NOTIFY_SOCKET"]
$stdout.reopen "listener.log", "a"
$stderr.reopen "listener.log", "a"
$stdout.sync = true
$stderr.sync = true
end

class StopLight
RED_LED = "16"
YELLOW_LED = "15"
GREEN_LED = "14"
HOST = "buildlight.collectiveidea.com"
DEVICE_ID = "collectiveidea-office"

@red = true
@yellow = true
@green = true
@blink_state = true
@running = true

def self.startup
export_pin(RED_LED)
export_pin(YELLOW_LED)
export_pin(GREEN_LED)

trap_signals
SdNotify.ready
setup_watchdog
start_client
end

def self.set_colors(colors)
@red = colors["red"]
@yellow = colors["yellow"]
@green = colors["green"]
start_light_thread
end

def self.write_colors
write_color(RED_LED, @red)
write_color(YELLOW_LED, @yellow && @blink_state)
write_color(GREEN_LED, @green)
end

def self.write_color(pin, value)
File.write("/sys/class/gpio/gpio#{pin}/value", value ? "1" : "0")
end

def self.export_pin(pin)
File.write("/sys/class/gpio/export", pin) unless File.exist?("/sys/class/gpio/gpio#{pin}/value")
end

def self.start_light_thread
return if @started_light_thread

Thread.new do
loop do
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
write_colors
@blink_state = !@blink_state
SdNotify.status("{red: #{@red}, yellow: #{@yellow}, green: #{@green}}")
sleep Process.clock_gettime(Process::CLOCK_MONOTONIC) - start + 1
end
end
@started_light_thread = true
end

def self.setup_watchdog
return unless SdNotify.watchdog?

usec = Integer(ENV["WATCHDOG_USEC"])

sec_f = usec / 1_000_000.0
# "It is recommended that a daemon sends a keep-alive notification message
# to the service manager every half of the time returned here."
ping_f = sec_f / 2

Thread.new do
puts "[#{Time.current}] Pinging systemd watchdog every #{ping_f.round(1)} sec"
loop do
sleep ping_f
SdNotify.watchdog
end
end
end

def self.start_client
params = {channel: "DeviceChannel", id: DEVICE_ID}
headers = {"ORIGIN" => "https://#{HOST}"}
EventMachine.run do
client = ActionCableClient.new("wss://#{HOST}/cable", params, true, headers)
client.connected { puts "[#{Time.current}] successfully connected." }
client.received do |message|
puts "[#{Time.current}] #{message["message"]["colors"]}"
set_colors(message["message"]["colors"])
end
client.disconnected { restart }
end
end

def self.trap_signals
Signal.trap("SIGTERM") { shutdown }
Signal.trap("SIGHUP") { shutdown }
Signal.trap("SIGINT") { shutdown }
Signal.trap("SIGUSR1") { restart }
Signal.trap("SIGUSR2") { restart }
end

def self.shutdown
SdNotify.stopping
@running = false
exit
end

def self.restart
return unless @running

puts "[#{Time.current}] restarting"
SdNotify.reloading
Kernel.exec(__FILE__)
end
end

StopLight.startup
26 changes: 26 additions & 0 deletions pi/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Load Raspberry Pi OS Lite (64 bit)

sudo apt update
sudo apt dist-upgrade
sudo reboot

sudo apt install libcurl4-openssl-dev git libreadline6-dev libssl-dev libyaml-dev libxml2-dev libxslt-dev autoconf ncurses-dev automake libtool bison

wget https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.1.tar.gz
tar xzf ruby-3.2.1.tar.gz
cd ruby-3.2.1/
./configure --prefix=/usr/local --enable-shared --disable-install-doc
make -j 2
sudo make install

mkdir listener
copy listener.rb Gemfile Gemfile.lock
chmod +x listener.rb
bundle config set --local path 'vendor'
bundle install

# Setup listener to load via systemd
copy buildlight_listener.service to /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable buildlight_listener.service
sudo systemctl start buildlight_listener.service

0 comments on commit b3a54f7

Please sign in to comment.