Compare commits
8 Commits
9470623915
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ff22cf6ec | |||
| b4dd203718 | |||
| 2eaaf8d6d8 | |||
|
|
a5aa6de67b | ||
|
|
d5a4a8c791 | ||
|
|
5b7b913588 | ||
|
|
ca5a4a1b5d | ||
| 9f7f8a4d80 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,4 +2,4 @@
|
||||
.config
|
||||
.project
|
||||
*.pid
|
||||
data
|
||||
config/smtp.yml
|
||||
|
||||
18
Dockerfile
18
Dockerfile
@@ -1,4 +1,4 @@
|
||||
FROM ruby:2.7
|
||||
FROM ruby:3.1
|
||||
|
||||
ENV BUILD_PACKAGES="apt-utils build-essential curl less nodejs sudo wget zsh libmariadb-dev libserialport-dev cron"
|
||||
|
||||
@@ -14,8 +14,18 @@ COPY Gemfile Gemfile.lock ./
|
||||
RUN \
|
||||
apt-get update -qq && \
|
||||
apt-get install -y $BUILD_PACKAGES && \
|
||||
bundle install
|
||||
# Fix bundler version BEFORE bundle install
|
||||
gem install bundler -v "$(grep -A 1 'BUNDLED WITH' Gemfile.lock | tail -n 1)" && \
|
||||
bundle install && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY . .
|
||||
# Copy cron file (if you use /etc/cron.d style)
|
||||
COPY reportcron /etc/cron.d/reportcron
|
||||
RUN chmod 0644 /etc/cron.d/reportcron && touch /var/log/cron.log
|
||||
|
||||
# Add entrypoint script
|
||||
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
|
||||
CMD ["/bin/bash -c ruby ./smartmeter.rb"]
|
||||
|
||||
1
Gemfile
1
Gemfile
@@ -12,3 +12,4 @@ gem 'numo-narray'
|
||||
gem 'i18n'
|
||||
gem 'gruff'
|
||||
gem 'net-http' # to avoid error: uninitialized constant ...
|
||||
gem 'mqtt'
|
||||
|
||||
67
Gemfile.lock
67
Gemfile.lock
@@ -1,13 +1,13 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activemodel (7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
activerecord (7.1.5.1)
|
||||
activemodel (= 7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
activemodel (7.1.6)
|
||||
activesupport (= 7.1.6)
|
||||
activerecord (7.1.6)
|
||||
activemodel (= 7.1.6)
|
||||
activesupport (= 7.1.6)
|
||||
timeout (>= 0.4.0)
|
||||
activesupport (7.1.5.1)
|
||||
activesupport (7.1.6)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
@@ -20,53 +20,55 @@ GEM
|
||||
mutex_m
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0)
|
||||
base64 (0.2.0)
|
||||
benchmark (0.4.0)
|
||||
bigdecimal (3.1.9)
|
||||
concurrent-ruby (1.3.4)
|
||||
connection_pool (2.4.1)
|
||||
base64 (0.3.0)
|
||||
benchmark (0.5.0)
|
||||
bigdecimal (4.1.2)
|
||||
concurrent-ruby (1.3.6)
|
||||
connection_pool (2.5.5)
|
||||
daemons (1.4.1)
|
||||
date (3.4.1)
|
||||
drb (2.2.1)
|
||||
et-orbi (1.2.11)
|
||||
date (3.5.1)
|
||||
drb (2.2.3)
|
||||
et-orbi (1.4.0)
|
||||
tzinfo
|
||||
fugit (1.11.1)
|
||||
et-orbi (~> 1, >= 1.2.11)
|
||||
fugit (1.12.1)
|
||||
et-orbi (~> 1.4)
|
||||
raabro (~> 1.4)
|
||||
gruff (0.24.0)
|
||||
histogram
|
||||
rmagick (>= 5.3)
|
||||
histogram (0.2.4.1)
|
||||
i18n (1.14.6)
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
logger (1.6.4)
|
||||
mail (2.8.1)
|
||||
logger (1.7.0)
|
||||
mail (2.9.0)
|
||||
logger
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.8)
|
||||
minitest (5.25.4)
|
||||
minitest (5.26.1)
|
||||
mqtt (0.7.0)
|
||||
logger
|
||||
mutex_m (0.3.0)
|
||||
mysql2 (0.5.6)
|
||||
net-http (0.6.0)
|
||||
uri
|
||||
net-imap (0.4.18)
|
||||
mysql2 (0.5.7)
|
||||
bigdecimal
|
||||
net-http (0.9.1)
|
||||
uri (>= 0.11.1)
|
||||
net-imap (0.4.24)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
net-protocol
|
||||
net-protocol (0.2.2)
|
||||
timeout
|
||||
net-smtp (0.5.0)
|
||||
net-smtp (0.5.1)
|
||||
net-protocol
|
||||
nokogiri (1.15.7)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
nokogiri (1.15.7-aarch64-linux)
|
||||
racc (~> 1.4)
|
||||
numo-narray (0.9.2.1)
|
||||
observer (0.1.2)
|
||||
pkg-config (1.5.8)
|
||||
pkg-config (1.6.5)
|
||||
raabro (1.4.0)
|
||||
racc (1.8.1)
|
||||
rmagick (5.5.0)
|
||||
@@ -75,12 +77,12 @@ GEM
|
||||
rufus-scheduler (3.9.2)
|
||||
fugit (~> 1.1, >= 1.11.1)
|
||||
securerandom (0.3.2)
|
||||
serialport (1.3.2)
|
||||
serialport (1.4.0)
|
||||
state_pattern (2.0.2)
|
||||
timeout (0.4.3)
|
||||
timeout (0.6.1)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uri (1.0.2)
|
||||
uri (1.1.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -91,6 +93,7 @@ DEPENDENCIES
|
||||
gruff
|
||||
i18n
|
||||
mail
|
||||
mqtt
|
||||
mysql2
|
||||
net-http
|
||||
nokogiri
|
||||
|
||||
18
LICENSE
Normal file
18
LICENSE
Normal file
@@ -0,0 +1,18 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 halteren
|
||||
|
||||
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.
|
||||
2
Rakefile
2
Rakefile
@@ -11,7 +11,7 @@ namespace :db do
|
||||
task :migrate do
|
||||
connection_details = YAML::load(File.open('config/database.yml'))
|
||||
ActiveRecord::Base.establish_connection(connection_details)
|
||||
ActiveRecord::Migrator.migrate("db/migrate/")
|
||||
ActiveRecord::MigrationContext.new("db/migrate/").migrate
|
||||
end
|
||||
|
||||
desc "Create the db"
|
||||
|
||||
@@ -94,14 +94,10 @@ class InSyncState < StatePattern::State
|
||||
reading.save
|
||||
end
|
||||
|
||||
# Write to EmonHub
|
||||
begin
|
||||
TCPSocket.open("printserver",5050){|s|
|
||||
s.write(sprintf("8 %d %d\r\n", reading.current_kw_consumed*1000, reading.current_kw_produced*1000))
|
||||
}
|
||||
rescue
|
||||
p "Socket problem."
|
||||
end
|
||||
# Post current_kw_consumed and current_kw_produced to MQTT
|
||||
MqttClient.publish("home/energy/current_kw_consumed", reading.current_kw_consumed.to_s)
|
||||
MqttClient.publish("home/energy/current_kw_produced", reading.current_kw_produced.to_s)
|
||||
|
||||
# Result
|
||||
return reading
|
||||
|
||||
|
||||
77
app/helpers/MqttClient.rb
Normal file
77
app/helpers/MqttClient.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
begin
|
||||
require "mqtt"
|
||||
rescue LoadError
|
||||
# Fallback to mosquitto_pub when the mqtt gem is not available.
|
||||
end
|
||||
|
||||
module MqttClient
|
||||
class << self
|
||||
def publish(topic, payload, retain: false, qos: 0)
|
||||
return false if topic.nil? || topic.empty?
|
||||
|
||||
if defined?(MQTT)
|
||||
publish_with_ruby_mqtt(topic, payload, retain, qos)
|
||||
else
|
||||
publish_with_mosquitto_pub(topic, payload, retain, qos)
|
||||
end
|
||||
rescue StandardError => e
|
||||
warn("[MqttClient] publish failed: #{e.class}: #{e.message}")
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def publish_with_ruby_mqtt(topic, payload, retain, qos)
|
||||
options = {
|
||||
host: mqtt_host,
|
||||
port: mqtt_port,
|
||||
keep_alive: 30
|
||||
}
|
||||
|
||||
options[:username] = mqtt_username if mqtt_username
|
||||
options[:password] = mqtt_password if mqtt_password
|
||||
|
||||
MQTT::Client.connect(options) do |client|
|
||||
client.publish(topic, payload.to_s, retain, qos)
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def publish_with_mosquitto_pub(topic, payload, retain, qos)
|
||||
command = [
|
||||
"mosquitto_pub",
|
||||
"-h", mqtt_host,
|
||||
"-p", mqtt_port.to_s,
|
||||
"-q", qos.to_s,
|
||||
"-t", topic,
|
||||
"-m", payload.to_s
|
||||
]
|
||||
|
||||
command << "-r" if retain
|
||||
|
||||
ok = system(*command)
|
||||
warn("[MqttClient] mosquitto_pub failed") unless ok
|
||||
ok
|
||||
end
|
||||
|
||||
def mqtt_host
|
||||
ENV.fetch("MQTT_HOST", "localhost")
|
||||
end
|
||||
|
||||
def mqtt_port
|
||||
Integer(ENV.fetch("MQTT_PORT", "1883"))
|
||||
rescue ArgumentError
|
||||
1883
|
||||
end
|
||||
|
||||
def mqtt_username
|
||||
v = ENV["MQTT_USERNAME"]
|
||||
v.nil? || v.strip.empty? ? nil : v
|
||||
end
|
||||
|
||||
def mqtt_password
|
||||
ENV["MQTT_PASSWORD"]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
class CreatesPrices << ActiveRecord::Migration[4.2]
|
||||
class CreatesPrices < ActiveRecord::Migration[4.2]
|
||||
def change
|
||||
create_table :prices do |t|
|
||||
t.datetime :hour
|
||||
@@ -7,4 +7,5 @@ class CreatesPrices << ActiveRecord::Migration[4.2]
|
||||
end
|
||||
|
||||
add_index :prices, :hour
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
restart: unless-stopped
|
||||
image: mysql:8.3
|
||||
volumes:
|
||||
- /home/pcog/smartmeter/data:/var/lib/mysql
|
||||
- mysql-data:/var/lib/mysql
|
||||
ports:
|
||||
- 3306:3306
|
||||
environment:
|
||||
@@ -12,24 +12,19 @@ services:
|
||||
MYSQL_DATABASE: smartmeter
|
||||
smartmeter:
|
||||
container_name: smartmeter
|
||||
labels:
|
||||
- diun.enable=false
|
||||
restart: unless-stopped
|
||||
build: .
|
||||
environment:
|
||||
MQTT_HOST: 10.0.0.240
|
||||
command: 'ruby ./smartmeter.rb'
|
||||
devices:
|
||||
- "/dev/ttyUSB1:/dev/ttyUSB0"
|
||||
- "/dev/ttyUSB0:/dev/ttyUSB0"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- db
|
||||
rstudio:
|
||||
container_name: rstudio
|
||||
restart: unless-stopped
|
||||
build: ./rstudio
|
||||
environment:
|
||||
PASSWORD: secret
|
||||
volumes:
|
||||
- ./rstudio:/home/rstudio/smartmeter
|
||||
ports:
|
||||
- 8787:8787
|
||||
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
|
||||
10
docker-entrypoint.sh
Normal file
10
docker-entrypoint.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
unset BUNDLE_PATH
|
||||
unset BUNDLE_BIN
|
||||
|
||||
# Start cron in background
|
||||
cron
|
||||
|
||||
# Start your Ruby process in foreground
|
||||
ruby /usr/src/app/smartmeter.rb
|
||||
3
reportcron
Normal file
3
reportcron
Normal file
@@ -0,0 +1,3 @@
|
||||
PATH=/usr/local/bundle/bin:/usr/local/bundle/gems/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
GEM_HOME=/usr/local/bundle
|
||||
1 7 * * * root cd /usr/src/app && bundle exec ruby report_mailer.rb >> /var/log/cron.log 2>&1
|
||||
Reference in New Issue
Block a user