Skip to content

Commit

Permalink
Merge branch 'release/0.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ledermann committed Sep 13, 2024
2 parents f2ddb20 + 4ccc6b8 commit 37966f0
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ MAPPING_0_TOPIC=senec/0/ENERGY/GUI_INVERTER_POWER
MAPPING_0_MEASUREMENT=PV
MAPPING_0_FIELD=inverter_power
MAPPING_0_TYPE=integer
MAPPING_0_MIN=5
MAPPING_0_MAX=15000
#
MAPPING_1_TOPIC=senec/0/ENERGY/GUI_HOUSE_POW
MAPPING_1_MEASUREMENT=PV
Expand Down
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ MAPPING_0_TOPIC=senec/0/ENERGY/GUI_INVERTER_POWER
MAPPING_0_MEASUREMENT=PV
MAPPING_0_FIELD=inverter_power
MAPPING_0_TYPE=integer
MAPPING_0_MIN=5
MAPPING_0_MAX=15000
#
MAPPING_1_TOPIC=senec/0/ENERGY/GUI_HOUSE_POW
MAPPING_1_MEASUREMENT=PV
Expand Down
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.4
3.3.5
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ruby:3.3.4-alpine AS builder
FROM ruby:3.3.5-alpine AS builder
RUN apk add --no-cache build-base

WORKDIR /mqtt-collector
Expand All @@ -8,7 +8,7 @@ RUN bundle config --local frozen 1 && \
bundle install -j4 --retry 3 && \
bundle clean --force

FROM ruby:3.3.4-alpine
FROM ruby:3.3.5-alpine
LABEL maintainer="georg@ledermann.dev"

# Add tzdata to get correct timezone
Expand Down
9 changes: 9 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ gem 'mqtt'
# CSV Reading and Writing (https://github.com/ruby/csv)
gem 'csv'

# Provides a simple logging utility for outputting messages. (https://github.com/ruby/logger)
gem 'logger'

# Support for encoding and decoding binary data using a Base64 representation. (https://github.com/ruby/base64)
gem 'base64'

# Class to build custom data structures, similar to a Hash. (https://github.com/ruby/ostruct)
gem 'ostruct'

# A formula language parser and evaluator (http://github.com/rubysolo/dentaku)
gem 'dentaku'

Expand Down
26 changes: 14 additions & 12 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ GEM
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.1)
lumberjack (1.2.10)
method_source (1.1.0)
mqtt (0.6.0)
Expand All @@ -52,8 +53,9 @@ GEM
notiffany (0.1.3)
nenv (~> 0.1)
shellany (~> 0.0)
ostruct (0.6.0)
parallel (1.26.3)
parser (3.3.4.2)
parser (3.3.5.0)
ast (~> 2.4.1)
racc
pry (0.14.2)
Expand All @@ -67,50 +69,47 @@ GEM
rb-inotify (0.11.1)
ffi (~> 1.0)
regexp_parser (2.9.2)
rexml (3.3.6)
strscan
rexml (3.3.7)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-core (3.13.1)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.2)
rspec-expectations (3.13.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.1)
rubocop (1.65.1)
rubocop (1.66.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.4, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-ast (>= 1.32.2, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.32.1)
rubocop-ast (1.32.3)
parser (>= 3.3.1.0)
rubocop-performance (1.21.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
rubocop-rspec (3.0.4)
rubocop-rspec (3.0.5)
rubocop (~> 1.61)
ruby-progressbar (1.13.0)
shellany (0.0.1)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov-html (0.13.1)
simplecov_json_formatter (0.1.4)
strscan (3.1.0)
thor (1.3.2)
unicode-display_width (2.5.0)
vcr (6.3.1)
Expand All @@ -125,13 +124,16 @@ PLATFORMS

DEPENDENCIES
amazing_print
base64
csv
dentaku
dotenv
guard-rspec
influxdb-client
jsonpath
logger
mqtt
ostruct
rake
rspec
rubocop
Expand Down
5 changes: 3 additions & 2 deletions lib/loop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ def next_message
# There is no timestamp in the MQTT message, so we use the current time
time = Time.now

records = mapper.records_for(topic, message)

# Log all the data we received
logger.info "# Message from #{time}"
logger.info " topic = #{topic}"
logger.info " message = #{message}"

# Convert the message to records
records = mapper.records_for(topic, message)

# Log all the data we are going to push to InfluxDB
records.each do |record|
logger.info " => #{record[:measurement]}:#{record[:field]} = #{record[:value]}"
Expand Down
64 changes: 53 additions & 11 deletions lib/mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def formatted_mapping(topic)
"#{mapping[:measurement]}:#{mapping[:field]}"
end

result += " (#{mapping[:type]})"
result += " (#{"#{mapping[:min]} ≥ " if mapping[:min]}#{mapping[:type]}#{" ≤ #{mapping[:max]}" if mapping[:max]})"
result
end
.join(', ')
Expand Down Expand Up @@ -74,24 +74,50 @@ def value_from(message, mapping)
def convert_type(message, mapping)
case mapping[:type]
when 'float'
begin
convert_float(message, mapping)
when 'integer'
convert_integer(message, mapping)
when 'boolean'
convert_boolean(message, mapping)
when 'string'
convert_string(message, mapping)
end
end

def convert_float(message, mapping)
ensure_min_max(
field: mapping[:field],
value: (begin
message.to_f
rescue StandardError
config.logger.warn "Failed to convert #{message} to float"
nil
end
when 'integer'
begin
end),
min: mapping[:min]&.to_f,
max: mapping[:max]&.to_f,
)
end

def convert_integer(message, mapping)
ensure_min_max(
field: mapping[:field],
value: (begin
message.to_f.round
rescue StandardError
config.logger.warn "Failed to convert #{message} to integer"
nil
end
when 'boolean'
%w[true ok yes on 1].include?(message.to_s.downcase)
when 'string'
message.to_s
end
end),
min: mapping[:min]&.to_i,
max: mapping[:max]&.to_i,
)
end

def convert_boolean(message, _mapping)
%w[true ok yes on 1].include?(message.to_s.downcase)
end

def convert_string(message, _mapping)
message.to_s
end

def extract_from_json(message, mapping)
Expand Down Expand Up @@ -143,4 +169,20 @@ def map_default(mapping, value)
def mappings_for(topic)
config.mappings.select { |mapping| mapping[:topic] == topic }
end

def ensure_min_max(field:, value:, min:, max:)
return value unless value && (max || min)

if max && value > max
config.logger.warn " Ignoring #{field}: #{value} exceeds maximum of #{max}"
return
end

if min && value < min
config.logger.warn " Ignoring #{field}: #{value} is below minimum of #{min}"
return
end

value
end
end
22 changes: 21 additions & 1 deletion spec/lib/mapper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
'MAPPING_0_MEASUREMENT' => 'PV',
'MAPPING_0_FIELD' => 'inverter_power',
'MAPPING_0_TYPE' => 'integer',
'MAPPING_0_MIN' => '5',
'MAPPING_0_MAX' => '15000',
#
'MAPPING_1_TOPIC' => 'senec/0/ENERGY/GUI_HOUSE_POW',
'MAPPING_1_MEASUREMENT' => 'PV',
Expand Down Expand Up @@ -176,7 +178,7 @@

it 'formats mapping' do
expect(mapper.formatted_mapping('senec/0/ENERGY/GUI_INVERTER_POWER')).to eq(
'PV:inverter_power (integer)',
'PV:inverter_power (5 ≥ integer ≤ 15000)',
)

expect(
Expand Down Expand Up @@ -443,6 +445,24 @@
expect(hash).to eq([])
end

it 'handles value < minimum' do
hash = mapper.records_for('senec/0/ENERGY/GUI_INVERTER_POWER', '-10')

expect(hash).to eq(
[],
)
expect(logger.warn_messages).to include(/Ignoring inverter_power: -10 is below minimum of 5/)
end

it 'handles value > maximum' do
hash = mapper.records_for('senec/0/ENERGY/GUI_INVERTER_POWER', '16000')

expect(hash).to eq(
[],
)
expect(logger.warn_messages).to include(/Ignoring inverter_power: 16000 exceeds maximum of 15000/)
end

it 'raises on unknown topic' do
expect do
mapper.records_for('this/is/an/unknown/topic', 'foo!')
Expand Down

0 comments on commit 37966f0

Please sign in to comment.