Compare commits

...

18 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
=
d5a4a8c791 Merge branch 'master' 2026-03-23 17:51:23 +01:00
=
5b7b913588 ignore data folder 2026-03-23 16:25:59 +01:00
=
ca5a4a1b5d Fixes active record 7 compatibility 2026-03-23 16:23:15 +01:00
9f7f8a4d80 Initial commit 2026-03-23 16:13:52 +01:00
9470623915 Fix Oxxio rates 2026 2026-01-03 21:07:29 +01:00
=
72cd644528 Fix: Interpolate missing ENTSOE price data points
Handle missing hourly price data from ENTSOE API by interpolating
values from adjacent time slots instead of returning nil.
2026-01-03 20:42:22 +01:00
85eaceaad2 New blurp for new meter 2026-01-03 20:11:49 +01:00
b40f4d2dc6 Add 2026 tarifs and rates 2026-01-03 19:58:03 +01:00
Aart van Halteren
24dd351249 Convert gas timestamp to datetime object 2025-07-17 13:11:28 +02:00
Aart van Halteren
63b2260ba0 refactor 2025-07-17 12:49:46 +02:00
Aart van Halteren
211b6e7cac debug info 2025-07-17 12:46:16 +02:00
Aart van Halteren
968a5cea2f two stage matching 2025-07-17 12:42:13 +02:00
Aart van Halteren
c165f24632 extra test 2025-07-17 12:37:41 +02:00
Aart van Halteren
b303331581 bug fix 2025-07-17 12:32:28 +02:00
16 changed files with 424 additions and 194 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
.config .config
.project .project
*.pid *.pid
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" 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 \ RUN \
apt-get update -qq && \ apt-get update -qq && \
apt-get install -y $BUILD_PACKAGES && \ 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 'i18n'
gem 'gruff' gem 'gruff'
gem 'net-http' # to avoid error: uninitialized constant ... gem 'net-http' # to avoid error: uninitialized constant ...
gem 'mqtt'

View File

@@ -1,13 +1,13 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activemodel (7.1.5.1) activemodel (7.1.6)
activesupport (= 7.1.5.1) activesupport (= 7.1.6)
activerecord (7.1.5.1) activerecord (7.1.6)
activemodel (= 7.1.5.1) activemodel (= 7.1.6)
activesupport (= 7.1.5.1) activesupport (= 7.1.6)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activesupport (7.1.5.1) activesupport (7.1.6)
base64 base64
benchmark (>= 0.3) benchmark (>= 0.3)
bigdecimal bigdecimal
@@ -20,53 +20,55 @@ GEM
mutex_m mutex_m
securerandom (>= 0.3) securerandom (>= 0.3)
tzinfo (~> 2.0) tzinfo (~> 2.0)
base64 (0.2.0) base64 (0.3.0)
benchmark (0.4.0) benchmark (0.5.0)
bigdecimal (3.1.9) bigdecimal (4.1.2)
concurrent-ruby (1.3.4) concurrent-ruby (1.3.6)
connection_pool (2.4.1) connection_pool (2.5.5)
daemons (1.4.1) daemons (1.4.1)
date (3.4.1) date (3.5.1)
drb (2.2.1) drb (2.2.3)
et-orbi (1.2.11) et-orbi (1.4.0)
tzinfo tzinfo
fugit (1.11.1) fugit (1.12.1)
et-orbi (~> 1, >= 1.2.11) et-orbi (~> 1.4)
raabro (~> 1.4) raabro (~> 1.4)
gruff (0.24.0) gruff (0.24.0)
histogram histogram
rmagick (>= 5.3) rmagick (>= 5.3)
histogram (0.2.4.1) histogram (0.2.4.1)
i18n (1.14.6) i18n (1.14.8)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
logger (1.6.4) logger (1.7.0)
mail (2.8.1) mail (2.9.0)
logger
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.8) minitest (5.26.1)
minitest (5.25.4) mqtt (0.7.0)
logger
mutex_m (0.3.0) mutex_m (0.3.0)
mysql2 (0.5.6) mysql2 (0.5.7)
net-http (0.6.0) bigdecimal
uri net-http (0.9.1)
net-imap (0.4.18) uri (>= 0.11.1)
net-imap (0.4.24)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
net-protocol net-protocol
net-protocol (0.2.2) net-protocol (0.2.2)
timeout timeout
net-smtp (0.5.0) net-smtp (0.5.1)
net-protocol net-protocol
nokogiri (1.15.7) nokogiri (1.15.7-aarch64-linux)
mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
numo-narray (0.9.2.1) numo-narray (0.9.2.1)
observer (0.1.2) observer (0.1.2)
pkg-config (1.5.8) pkg-config (1.6.5)
raabro (1.4.0) raabro (1.4.0)
racc (1.8.1) racc (1.8.1)
rmagick (5.5.0) rmagick (5.5.0)
@@ -75,12 +77,12 @@ GEM
rufus-scheduler (3.9.2) rufus-scheduler (3.9.2)
fugit (~> 1.1, >= 1.11.1) fugit (~> 1.1, >= 1.11.1)
securerandom (0.3.2) securerandom (0.3.2)
serialport (1.3.2) serialport (1.4.0)
state_pattern (2.0.2) state_pattern (2.0.2)
timeout (0.4.3) timeout (0.6.1)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uri (1.0.2) uri (1.1.1)
PLATFORMS PLATFORMS
ruby ruby
@@ -91,6 +93,7 @@ DEPENDENCIES
gruff gruff
i18n i18n
mail mail
mqtt
mysql2 mysql2
net-http net-http
nokogiri nokogiri

18
LICENSE Normal file
View 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.

View File

@@ -11,7 +11,7 @@ namespace :db do
task :migrate do task :migrate do
connection_details = YAML::load(File.open('config/database.yml')) connection_details = YAML::load(File.open('config/database.yml'))
ActiveRecord::Base.establish_connection(connection_details) ActiveRecord::Base.establish_connection(connection_details)
ActiveRecord::Migrator.migrate("db/migrate/") ActiveRecord::MigrationContext.new("db/migrate/").migrate
end end
desc "Create the db" desc "Create the db"

View File

@@ -47,7 +47,6 @@ class InSyncState < StatePattern::State
end end
def handle_frame(frame_lines) def handle_frame(frame_lines)
gas_pattern = /^([0-1:24\.2\.1]+)\((\d{12}[SW])\)\((\d{5}\.\d{3})\*m3\)$/
# prepare DB record # prepare DB record
last_reading = Reading.last last_reading = Reading.last
@@ -79,9 +78,12 @@ class InSyncState < StatePattern::State
end end
if line.match(/0-0:96.14.0/) # Hoog/laag tarief if line.match(/0-0:96.14.0/) # Hoog/laag tarief
reading.high_tarif = line.split(/0-0:96.14.0\(|\)/).join.eql?("0002") reading.high_tarif = line.split(/0-0:96.14.0\(|\)/).join.eql?("0002")
end end
if match = line.match(/0-1:24.3.0/) # Gas verbruik (1x per uur een nieuwe stand) # example line: "0-1:24.2.1(250717121000S)(00000.474*m3)"
p "Gas reading: #{match[1]} (#{match[2]})" if match = line.match(/^(0-1:24.2.1)\(([^)]+)\)\(([\d.]+)\*m3\)$/) # Gas verbruik (1x per uur een nieuwe stand)
#p "Gas reading: #{match[1]} (#{match[2]})"
#datetime = DateTime.strptime(match[2][0..11], "%y%m%d%H%M%S")
#p "Gas reading at #{datetime}."
reading.total_m3_gas_consumed = match[3].to_f reading.total_m3_gas_consumed = match[3].to_f
end end
} }
@@ -92,15 +94,11 @@ class InSyncState < StatePattern::State
reading.save reading.save
end end
# Write to EmonHub # Post current_kw_consumed and current_kw_produced to MQTT
begin MqttClient.publish("home/energy/current_kw_consumed", reading.current_kw_consumed.to_s)
TCPSocket.open("printserver",5050){|s| MqttClient.publish("home/energy/current_kw_produced", reading.current_kw_produced.to_s)
s.write(sprintf("8 %d %d\r\n", reading.current_kw_consumed*1000, reading.current_kw_produced*1000))
} # Result
rescue
p "Socket problem."
end
# Result
return reading return reading
end end

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

@@ -6,8 +6,8 @@ EASY_ENERGY_TARIFFS = {}
# See https://www.belastingdienst.nl/wps/wcm/connect/bldcontentnl/belastingdienst/zakelijk/overige_belastingen/belastingen_op_milieugrondslag/tarieven_milieubelastingen/tabellen_tarieven_milieubelastingen # See https://www.belastingdienst.nl/wps/wcm/connect/bldcontentnl/belastingdienst/zakelijk/overige_belastingen/belastingen_op_milieugrondslag/tarieven_milieubelastingen/tabellen_tarieven_milieubelastingen
# Without VAT # Without VAT
ENERGY_TAX_KWH = { 2020 => 0.09770, 2021 => 0.09428, 2022 => 0.03679, 2023 => 0.12599, 2024 => 0.10880, 2025 => 0.10154} ENERGY_TAX_KWH = { 2020 => 0.09770, 2021 => 0.09428, 2022 => 0.03679, 2023 => 0.12599, 2024 => 0.10880, 2025 => 0.10154, 2026 => 0.09157 }
ODE_KWH = { 2020 => 0.0273, 2021 => 0.0300, 2022 => 0.0305, 2023 => 0.0, 2024 => 0.0, 2025 => 0.0} ODE_KWH = { 2020 => 0.0273, 2021 => 0.0300, 2022 => 0.0305, 2023 => 0.0, 2024 => 0.0, 2025 => 0.0, 2026 =>0.0}
# merge by adding values # merge by adding values
TAX_KWH = ENERGY_TAX_KWH.merge(ODE_KWH){|key, energy_tax, ode| energy_tax + ode} TAX_KWH = ENERGY_TAX_KWH.merge(ODE_KWH){|key, energy_tax, ode| energy_tax + ode}
@@ -82,10 +82,7 @@ class Cost
end end
when 2023 when 2023
0.018 0.018
when 2024 when 2024..2026
# opslag met BTW: 0,02178
0.018457
when 2025
# opslag met BTW: 0,02178 # opslag met BTW: 0,02178
0.018457 0.018457
end end
@@ -93,7 +90,8 @@ class Cost
def easy_energy_cost(formatted_hour, usage_kwh, return_kwh) def easy_energy_cost(formatted_hour, usage_kwh, return_kwh)
return nil if (usage_kwh.nil? || return_kwh.nil?) return nil if (usage_kwh.nil? || return_kwh.nil?)
#p "easy_energy_cost for " + formatted_hour
usage_kwh_cost = return_kwh_cost = (entsoe.price_at(formatted_hour)+easy_energy_rate(formatted_hour))*(1+vat_at(Date.parse(formatted_hour))) usage_kwh_cost = return_kwh_cost = (entsoe.price_at(formatted_hour)+easy_energy_rate(formatted_hour))*(1+vat_at(Date.parse(formatted_hour)))
add_tax(formatted_hour, usage_kwh, usage_kwh_cost, return_kwh, return_kwh_cost) add_tax(formatted_hour, usage_kwh, usage_kwh_cost, return_kwh, return_kwh_cost)
end end
@@ -260,6 +258,8 @@ class Cost
0.25767769 0.25767769
when 2025 when 2025
high_tariff ? 0.2695 : 0.2296 high_tariff ? 0.2695 : 0.2296
when 2026
high_tariff ? 0.23186 : 0.22442
end end
end end
@@ -303,6 +303,9 @@ class Cost
vat = 1 + vat_at(Date.parse(formatted_hour)) vat = 1 + vat_at(Date.parse(formatted_hour))
normaal_kwh_cost = 0.2695*vat normaal_kwh_cost = 0.2695*vat
dal_kwh_cost = 0.2296*vat dal_kwh_cost = 0.2296*vat
when 1767225600..1785887999 # 2026 full year
normaal_kwh_cost = 0.19161
dal_kwh_cost = 0.18547
else else
p "Not supported interval Oxxio for value: %d" % date.to_time.to_i p "Not supported interval Oxxio for value: %d" % date.to_time.to_i
# catch-all, incase 'formated_hour' is outside any of the cases # catch-all, incase 'formated_hour' is outside any of the cases

View File

@@ -103,15 +103,61 @@ class Entsoe
formatted_date = date.strftime("%F") formatted_date = date.strftime("%F")
doc = Nokogiri::XML(URI.open(url)) doc = Nokogiri::XML(URI.open(url))
#p "Entsoe prices: %s" % doc
prices = doc.xpath('.//xmlns:Point').map{|p| parse_point(p)} prices = doc.xpath('.//xmlns:Point').map{|p| parse_point(p)}
begin begin
# get start_time (in UTC) from XML docment # get start_time (in UTC) from XML docment
start_time = DateTime.parse(doc.xpath('.//xmlns:period.timeInterval//xmlns:start').text) start_time = DateTime.parse(doc.xpath('.//xmlns:period.timeInterval//xmlns:start').text)
# Get resolution to determine expected number of positions
resolution = doc.xpath('.//xmlns:resolution').text
interval_minutes = resolution == 'PT15M' ? 15 : 60
expected_positions = (24 * 60) / interval_minutes
# Create hash from available prices
price_hash = prices.to_h
# Fill in missing positions by interpolating from adjacent values
complete_prices = {}
(1..expected_positions).each do |position|
if price_hash.key?(position)
complete_prices[position] = price_hash[position]
else
# Find previous and next available prices for interpolation
prev_price = (position-1).downto(1).find { |p| price_hash.key?(p) }
next_price = (position+1).upto(expected_positions).find { |p| price_hash.key?(p) }
if prev_price && next_price
# Interpolate between previous and next
complete_prices[position] = ((price_hash[prev_price] + price_hash[next_price]) / 2.0).round(5)
elsif prev_price
# Use previous price as fallback
complete_prices[position] = price_hash[prev_price]
elsif next_price
# Use next price as fallback
complete_prices[position] = price_hash[next_price]
end
# Calculate the formatted hour for the warning message
hour_offset = interval_minutes == 15 ? (position - 1) / 4 : (position - 1)
missing_hour = start_time.advance(hours: hour_offset).in_time_zone(zone).strftime("%F %H")
p "Warning: Missing Entsoe data for #{missing_hour}, interpolated value: #{complete_prices[position]}"
end
end
#returns a hash with keys formatted "yyyy-mm-dd hr" and values price (per kwh) #returns a hash with keys formatted "yyyy-mm-dd hr" and values price (per kwh)
# <position> tag runs from 1-24. We need hours from 00-23, therefore substracting 1 # <position> tag runs from 1-96 for 15min intervals. Convert to hourly by taking first interval of each hour
prices.map{|p| [start_time.advance(hours: (p[0]-1)), p[1]]}.to_h if interval_minutes == 15
# For 15-minute intervals, use the first interval of each hour (positions 1, 5, 9, 13, ...)
complete_prices.select { |pos, _| (pos - 1) % 4 == 0 }
.map { |pos, price| [start_time.advance(hours: ((pos - 1) / 4)), price] }
.to_h
else
# For hourly data (position runs from 1-24, we need hours from 00-23)
complete_prices.map { |pos, price| [start_time.advance(hours: (pos - 1)), price] }.to_h
end
rescue Date::Error => e rescue Date::Error => e
p e.message p e.message
{} {}

View File

@@ -1,4 +1,4 @@
class CreatesPrices << ActiveRecord::Migration[4.2] class CreatesPrices < ActiveRecord::Migration[4.2]
def change def change
create_table :prices do |t| create_table :prices do |t|
t.datetime :hour t.datetime :hour
@@ -7,4 +7,5 @@ class CreatesPrices << ActiveRecord::Migration[4.2]
end end
add_index :prices, :hour add_index :prices, :hour
end
end end

View File

@@ -4,7 +4,7 @@ services:
restart: unless-stopped restart: unless-stopped
image: mysql:8.3 image: mysql:8.3
volumes: volumes:
- /home/pcog/smartmeter/data:/var/lib/mysql - mysql-data:/var/lib/mysql
ports: ports:
- 3306:3306 - 3306:3306
environment: environment:
@@ -12,24 +12,19 @@ services:
MYSQL_DATABASE: smartmeter MYSQL_DATABASE: smartmeter
smartmeter: smartmeter:
container_name: smartmeter container_name: smartmeter
labels:
- diun.enable=false
restart: unless-stopped restart: unless-stopped
build: . build: .
environment:
MQTT_HOST: 10.0.0.240
command: 'ruby ./smartmeter.rb' command: 'ruby ./smartmeter.rb'
devices: devices:
- "/dev/ttyUSB1:/dev/ttyUSB0" - "/dev/ttyUSB0:/dev/ttyUSB0"
volumes: volumes:
- .:/usr/src/app - .:/usr/src/app
depends_on: depends_on:
- db - 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

View File

@@ -1,121 +1,185 @@
3233)
/ISk5\2ME382-1003 1-0:1.8.1(001402.671*kWh)
1-0:1.8.2(001130.208*kWh)
0-0:96.1.1(4B413650303035313238303430383132) 1-0:2.8.1(000080.565*kWh)
1-0:1.8.1(00553.931*kWh) 1-0:2.8.2(000205.556*kWh)
1-0:1.8.2(00431.594*kWh) 0-0:96.14.0(0001)
1-0:2.8.1(00093.034*kWh) 1-0:1.7.0(01.283*kW)
1-0:2.8.2(00147.035*kWh) 1-0:2.7.0(00.000*kW)
0-0:96.14.0(0002) 0-0:96.7.21(00015)
1-0:1.7.0(0000.00*kW) 0-0:96.7.9(00004)
1-0:2.7.0(0000.28*kW) 1-0:99.97.0(0)(0-0:96.7.19)
0-0:17.0.0(0999.00*kW) 1-0:32.32.0(00000)
0-0:96.3.10(1) 1-0:32.36.0(00003)
0-0:96.13.1() 0-0:96.13.0()
0-0:96.13.0() 1-0:32.7.0(236.0*V)
0-1:24.1.0(3) 1-0:31.7.0(005*A)
0-1:96.1.0(3238303131303031323439333134383132) 1-0:21.7.0(01.283*kW)
0-1:24.3.0(130626090000)(00)(60)(1)(0-1:24.2.1)(m3) 1-0:22.7.0(00.000*kW)
(00309.466) 0-1:24.1.0(003)
0-1:24.4.0(1) 0-1:96.1.0(4730303933303034333734333338333235)
! 0-1:24.2.1(260103201000W)(00272.130*m3)
/ISk5\2ME382-1003 !7603
/CTA5ZIV-METER
0-0:96.1.1(4B413650303035313238303430383132)
1-0:1.8.1(00553.931*kWh) 1-3:2.671*kWh)
1-0:1.8.2(00431.594*kWh) 1-0:1.8.2(001130.208*kWh)
1-0:2.8.1(00093.034*kWh) 1-0:2.8.1(000080.565*kWh)
1-0:2.8.2(00147.036*kWh) 1-0:2.8.2(000205.556*kWh)
0-0:96.14.0(0002) 0-0:96.14.0(0001)
1-0:1.7.0(0000.00*kW) 1-0:1.7.0(01.286*kW)
1-0:2.7.0(0000.31*kW) 1-0:2.7.0(00.000*kW)
0-0:17.0.0(0999.00*kW) 0-0:96.7.21(00015)
0-0:96.3.10(1) 0-0:96.7.9(00004)
0-0:96.13.1() 1-0:99.97.0(0)(0-0:96.7.19)
0-0:96.13.0() 1-0:32.32.0(00000)
0-1:24.1.0(3) 1-0:32.36.0(00003)
0-1:96.1.0(3238303131303031323439333134383132) 0-0:96.13.0()
0-1:24.3.0(130626090000)(00)(60)(1)(0-1:24.2.1)(m3) 1-0:32.7.0(236.0*V)
(00309.466) 1-0:31.7.0(005*A)
0-1:24.4.0(1) 1-0:21.7.0(01.286*kW)
! 1-0:22.7.0(00.000*kW)
/ISk5\2ME382-1003 0-1:24.1.0(003)
0-1:96.1.0(4730303933303034333734333338333235)
0-0:96.1.1(4B413650303035313238303430383132) 0-1:24.2.1(260103201000W)(00272.130*m3)
1-0:1.8.1(00553.931*kWh) !B5E2
1-0:1.8.2(00431.594*kWh) /CTA5ZIV-METER
1-0:2.8.1(00093.034*kWh)
1-0:2.8.2(00147.037*kWh) 1-3:0.2.8(50)
0-0:96.14.0(0002) 0-0::1.8.2(001130.208*kWh)
1-0:1.7.0(0000.00*kW) 1-0:2.8.1(000080.565*kWh)
1-0:2.7.0(0000.32*kW) 1-0:2.8.2(000205.556*kWh)
0-0:17.0.0(0999.00*kW) 0-0:96.14.0(0001)
0-0:96.3.10(1) 1-0:1.7.0(01.335*kW)
0-0:96.13.1() 1-0:2.7.0(00.000*kW)
0-0:96.13.0() 0-0:96.7.21(00015)
0-1:24.1.0(3) 0-0:96.7.9(00004)
0-1:96.1.0(3238303131303031323439333134383132) 1-0:99.97.0(0)(0-0:96.7.19)
0-1:24.3.0(130626090000)(00)(60)(1)(0-1:24.2.1)(m3) 1-0:32.32.0(00000)
(00309.466) 1-0:32.36.0(00003)
0-1:24.4.0(1) 0-0:96.13.0()
! 1-0:32.7.0(236.0*V)
/ISk5\2ME382-1003 1-0:31.7.0(005*A)
1-0:21.7.0(01.335*kW)
0-0:96.1.1(4B413650303035313238303430383132) 1-0:22.7.0(00.000*kW)
1-0:1.8.1(00553.931*kWh) 0-1:24.1.0(003)
1-0:1.8.2(00431.594*kWh) 0-1:96.1.0(4730303933303034333734333338333235)
1-0:2.8.1(00093.034*kWh) 0-1:24.2.1(260103201000W)(00272.130*m3)
1-0:2.8.2(00147.038*kWh) !6D5D
0-0:96.14.0(0002) /CTA5ZIV-METER
1-0:1.7.0(0000.00*kW)
1-0:2.7.0(0000.32*kW) 1-3:0.2.8(50)
0-0:17.0.0(0999.00*kW) 0-0:1.0.0(260103201104W)
0-0:96.3.10(1)
0-0:96.13.1() 1-0:2.8.1(000080.565*kWh)
0-0:96.13.0() 1-0:2.8.2(000205.556*kWh)
0-1:24.1.0(3) 0-0:96.14.0(0001)
0-1:96.1.0(3238303131303031323439333134383132) 1-0:1.7.0(01.276*kW)
0-1:24.3.0(130626090000)(00)(60)(1)(0-1:24.2.1)(m3) 1-0:2.7.0(00.000*kW)
(00309.466) 0-0:96.7.21(00015)
0-1:24.4.0(1) 0-0:96.7.9(00004)
! 1-0:99.97.0(0)(0-0:96.7.19)
/ISk5\2ME382-1003 1-0:32.32.0(00000)
1-0:32.36.0(00003)
0-0:96.1.1(4B413650303035313238303430383132) 0-0:96.13.0()
1-0:1.8.1(00553.931*kWh) 1-0:32.7.0(236.0*V)
1-0:1.8.2(00431.594*kWh) 1-0:31.7.0(005*A)
1-0:2.8.1(00093.034*kWh) 1-0:21.7.0(01.276*kW)
1-0:2.8.2(00147.039*kWh) 1-0:22.7.0(00.000*kW)
0-0:96.14.0(0002) 0-1:24.1.0(003)
1-0:1.7.0(0000.00*kW) 0-1:96.1.0(4730303933303034333734333338333235)
1-0:2.7.0(0000.32*kW) 0-1:24.2.1(260103201000W)(00272.130*m3)
0-0:17.0.0(0999.00*kW) !FDB1
0-0:96.3.10(1) 3233)
0-0:96.13.1() 1-0:1.8.1(001402.673*kWh)
0-0:96.13.0() 1-0:1.8.2(001130.208*kWh)
0-1:24.1.0(3) 1-0:2.8.1(000080.565*kWh)
0-1:96.1.0(3238303131303031323439333134383132) 1-0:2.8.2(000205.556*kWh)
0-1:24.3.0(130626090000)(00)(60)(1)(0-1:24.2.1)(m3) 0-0:96.14.0(0001)
(00309.466) 1-0:1.7.0(01.344*kW)
0-1:24.4.0(1) 1-0:2.7.0(00.000*kW)
! 0-0:96.7.21(00015)
/ISk5\2ME382-1003 0-0:96.7.9(00004)
1-0:99.97.0(0)(0-0:96.7.19)
0-0:96.1.1(4B413650303035313238303430383132) 1-0:32.32.0(00000)
1-0:1.8.1(00553.931*kWh) 1-0:32.36.0(00003)
1-0:1.8.2(00431.594*kWh) 0-0:96.13.0()
1-0:2.8.1(00093.034*kWh) 1-0:32.7.0(236.0*V)
1-0:2.8.2(00147.040*kWh) 1-0:31.7.0(006*A)
0-0:96.14.0(0002) 1-0:21.7.0(01.344*kW)
1-0:1.7.0(0000.00*kW) 1-0:22.7.0(00.000*kW)
1-0:2.7.0(0000.30*kW) 0-1:24.1.0(003)
0-0:17.0.0(0999.00*kW) 0-1:96.1.0(4730303933303034333734333338333235)
0-0:96.3.10(1) 0-1:24.2.1(260103201000W)(00272.130*m3)
0-0:96.13.1() !E9B8
0-0:96.13.0() /CTA5ZIV-METER
0-1:24.1.0(3)
0-1:96.1.0(3238303131303031323439333134383132) 1-3:0.2.3*kWh)
0-1:24.3.0(130626090000)(00)(60)(1)(0-1:24.2.1)(m3) 1-0:1.8.2(001130.208*kWh)
(00309.466) 1-0:2.8.1(000080.565*kWh)
0-1:24.4.0(3) 1-0:2.8.2(000205.556*kWh)
! 0-0:96.14.0(0001)
1-0:1.7.0(01.319*kW)
1-0:2.7.0(00.000*kW)
0-0:96.7.21(00015)
0-0:96.7.9(00004)
1-0:99.97.0(0)(0-0:96.7.19)
1-0:32.32.0(00000)
1-0:32.36.0(00003)
0-0:96.13.0()
1-0:32.7.0(236.0*V)
1-0:31.7.0(005*A)
1-0:21.7.0(01.319*kW)
1-0:22.7.0(00.000*kW)
0-1:24.1.0(003)
0-1:96.1.0(4730303933303034333734333338333235)
0-1:24.2.1(260103201000W)(00272.130*m3)
!F0CD
/CTA5ZIV-METER
1-3:0.2.8(50)
0-0:1.0.0(2001130.208*kWh)
1-0:2.8.1(000080.565*kWh)
1-0:2.8.2(000205.556*kWh)
0-0:96.14.0(0001)
1-0:1.7.0(01.336*kW)
1-0:2.7.0(00.000*kW)
0-0:96.7.21(00015)
0-0:96.7.9(00004)
1-0:99.97.0(0)(0-0:96.7.19)
1-0:32.32.0(00000)
1-0:32.36.0(00003)
0-0:96.13.0()
1-0:32.7.0(236.0*V)
1-0:31.7.0(005*A)
1-0:21.7.0(01.336*kW)
1-0:22.7.0(00.000*kW)
0-1:24.1.0(003)
0-1:96.1.0(4730303933303034333734333338333235)
0-1:24.2.1(260103201000W)(00272.130*m3)
!CCE2
/CTA5ZIV-METER
1-3:0.2.8(50)
0-0:1.0.0(260103201108W)
0-0:96.1.1(4530303839303031303230373731393233)
1-0:1.8.1(001402.674*kWh)
1-0:1.8.2(001130.208*kWh)
1-0:2.8.1(000080.565*kWh)
1-0:2.8.2(000205.556*kWh)
0-0:96.14.0(0001)
1-0:1.7.0(01.319*kW)
1-0:2.7.0(00.000*kW)
0-0:96.7.21(00015)
0-0:96.7.9(00004)
1-0:99.97.0(0)(0-0:96.7.19)
1-0:32.32.0(00000)
1-0:32.36.0(00003)
0-0:96.13.0()
1-0:32.7.0(236.0*V)
1-0:31.7.0(005*A)
1-0:21.7.0(01.319*kW)
1-0:22.7.0(00.000*kW)
0-1:24.1.0-1:96.1.0(4730303933303034333734333338333235)
0-1:24.2.1(260103201000W)(00272.130*m3)
!B4B3

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