Compare commits

...

4 Commits

Author SHA1 Message Date
7ff22cf6ec No password in git 2026-04-26 14:21:48 +02:00
b4dd203718 No password in git 2026-04-26 14:19:54 +02:00
2eaaf8d6d8 Cronjob added inside container 2026-04-26 13:25:30 +02:00
=
a5aa6de67b Post to MQTT
Co-authored-by: Copilot <copilot@github.com>
2026-04-26 13:04:29 +02:00
10 changed files with 155 additions and 60 deletions

2
.gitignore vendored
View File

@@ -2,4 +2,4 @@
.config
.project
*.pid
data
config/smtp.yml

View File

@@ -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"]

View File

@@ -12,3 +12,4 @@ gem 'numo-narray'
gem 'i18n'
gem 'gruff'
gem 'net-http' # to avoid error: uninitialized constant ...
gem 'mqtt'

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -4,7 +4,7 @@ services:
restart: unless-stopped
image: mysql:8.3
volumes:
- $PWD/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"
devices:
- "/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
View 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
View 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