Skip to content

Commit

Permalink
Handle additional topics
Browse files Browse the repository at this point in the history
Belongs to #46
  • Loading branch information
ledermann committed Oct 7, 2023
1 parent 2f34baa commit 744d8d9
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 35 deletions.
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ MQTT_TOPIC_BAT_POWER=senec/0/ENERGY/GUI_BAT_DATA_POWER
MQTT_TOPIC_BAT_VOLTAGE=senec/0/ENERGY/GUI_BAT_DATA_VOLTAGE
MQTT_TOPIC_CASE_TEMP=senec/0/TEMPMEASURE/CASE_TEMP
MQTT_TOPIC_CURRENT_STATE=senec/0/ENERGY/STAT_STATE_Text
MQTT_TOPIC_CURRENT_STATE_CODE=senec/0/ENERGY/STAT_STATE
MQTT_TOPIC_APPLICATION_VERSION=senec/0/WIZARD/APPLICATION_VERSION
MQTT_TOPIC_MPP1_POWER=senec/0/PV1/MPP_POWER/0
MQTT_TOPIC_MPP2_POWER=senec/0/PV1/MPP_POWER/1
MQTT_TOPIC_MPP3_POWER=senec/0/PV1/MPP_POWER/2
MQTT_TOPIC_INVERTER_POWER=senec/0/ENERGY/GUI_INVERTER_POWER
MQTT_TOPIC_WALLBOX_CHARGE_POWER=senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
MQTT_TOPIC_WALLBOX_CHARGE_POWER0=senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
MQTT_TOPIC_WALLBOX_CHARGE_POWER1=senec/0/WALLBOX/APPARENT_CHARGING_POWER/1
MQTT_TOPIC_WALLBOX_CHARGE_POWER2=senec/0/WALLBOX/APPARENT_CHARGING_POWER/2
MQTT_TOPIC_WALLBOX_CHARGE_POWER3=senec/0/WALLBOX/APPARENT_CHARGING_POWER/3
MQTT_TOPIC_POWER_RATIO=senec/0/PV1/POWER_RATIO

# Example 2: For evcc
# MQTT_TOPIC_HOUSE_POW=evcc/site/homePower
Expand Down
8 changes: 7 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ MQTT_TOPIC_BAT_POWER=senec/0/ENERGY/GUI_BAT_DATA_POWER
MQTT_TOPIC_BAT_VOLTAGE=senec/0/ENERGY/GUI_BAT_DATA_VOLTAGE
MQTT_TOPIC_CASE_TEMP=senec/0/TEMPMEASURE/CASE_TEMP
MQTT_TOPIC_CURRENT_STATE=senec/0/ENERGY/STAT_STATE_Text
MQTT_TOPIC_CURRENT_STATE_CODE=senec/0/ENERGY/STAT_STATE
MQTT_TOPIC_APPLICATION_VERSION=senec/0/WIZARD/APPLICATION_VERSION
MQTT_TOPIC_MPP1_POWER=senec/0/PV1/MPP_POWER/0
MQTT_TOPIC_MPP2_POWER=senec/0/PV1/MPP_POWER/1
MQTT_TOPIC_MPP3_POWER=senec/0/PV1/MPP_POWER/2
MQTT_TOPIC_INVERTER_POWER=senec/0/ENERGY/GUI_INVERTER_POWER
MQTT_TOPIC_WALLBOX_CHARGE_POWER=senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
MQTT_TOPIC_WALLBOX_CHARGE_POWER0=senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
MQTT_TOPIC_WALLBOX_CHARGE_POWER1=senec/0/WALLBOX/APPARENT_CHARGING_POWER/1
MQTT_TOPIC_WALLBOX_CHARGE_POWER2=senec/0/WALLBOX/APPARENT_CHARGING_POWER/2
MQTT_TOPIC_WALLBOX_CHARGE_POWER3=senec/0/WALLBOX/APPARENT_CHARGING_POWER/3
MQTT_TOPIC_POWER_RATIO=senec/0/PV1/POWER_RATIO

# InfluxDB credentials
INFLUX_HOST=localhost
Expand Down
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Style/FrozenStringLiteralComment:
Metrics/MethodLength:
Max: 20

Metrics/ClassLength:
Max: 200

Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: consistent_comma

Expand Down
48 changes: 33 additions & 15 deletions app/config.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
require 'uri'

MQTT_TOPICS = %i[
inverter_power
mpp1_power
mpp2_power
mpp3_power
house_pow
bat_fuel_charge
wallbox_charge_power
wallbox_charge_power0
wallbox_charge_power1
wallbox_charge_power2
wallbox_charge_power3
bat_power
grid_pow
power_ratio
current_state
current_state_code
current_state_ok
application_version
case_temp
bat_charge_current
bat_voltage
].freeze

Config =
Struct.new(
# MQTT credentials
Expand All @@ -16,9 +40,17 @@
:mqtt_topic_house_pow,
:mqtt_topic_bat_fuel_charge,
:mqtt_topic_wallbox_charge_power,
:mqtt_topic_wallbox_charge_power0,
:mqtt_topic_wallbox_charge_power1,
:mqtt_topic_wallbox_charge_power2,
:mqtt_topic_wallbox_charge_power3,
:mqtt_topic_bat_power,
:mqtt_topic_grid_pow,
:mqtt_topic_power_ratio,
:mqtt_topic_current_state,
:mqtt_topic_current_state_code,
:mqtt_topic_current_state_ok,
:mqtt_topic_application_version,
:mqtt_topic_case_temp,
:mqtt_topic_bat_charge_current,
:mqtt_topic_bat_voltage,
Expand Down Expand Up @@ -55,21 +87,7 @@ def self.mqtt_credentials_from_env
end

def self.mqtt_topics_from_env
%i[
inverter_power
mpp1_power
mpp2_power
mpp3_power
house_pow
bat_fuel_charge
wallbox_charge_power
bat_power
grid_pow
current_state
case_temp
bat_charge_current
bat_voltage
].each_with_object({}) do |topic, hash|
MQTT_TOPICS.each_with_object({}) do |topic, hash|
value = ENV.fetch("MQTT_TOPIC_#{topic.to_s.upcase}", nil)
next unless value

Expand Down
40 changes: 40 additions & 0 deletions app/mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@ def map_wallbox_charge_power(value)
{ 'wallbox_charge_power' => value.to_f.round }
end

def map_wallbox_charge_power0(value)
{ 'wallbox_charge_power0' => value.to_f.round }
end

def map_wallbox_charge_power1(value)
{ 'wallbox_charge_power1' => value.to_f.round }
end

def map_wallbox_charge_power2(value)
{ 'wallbox_charge_power2' => value.to_f.round }
end

def map_wallbox_charge_power3(value)
{ 'wallbox_charge_power3' => value.to_f.round }
end

def map_bat_power(value)
value = value.to_f.round
value = -value if config.mqtt_flip_bat_power
Expand All @@ -74,6 +90,14 @@ def map_bat_power(value)
end
end

def map_bat_voltage(value)
{ 'bat_voltage' => value.to_f }
end

def map_bat_charge_current(value)
{ 'bat_charge_current' => value.to_f }
end

def map_grid_pow(value)
value = value.to_f.round

Expand All @@ -92,7 +116,23 @@ def map_current_state(value)
{ 'current_state' => value }
end

def map_current_state_code(value)
{ 'current_state_code' => value.to_i }
end

def map_current_state_ok(value)
{ 'current_state_ok' => value.in?(%w[true 1 OK]) }
end

def map_case_temp(value)
{ 'case_temp' => value.to_f.round(1) }
end

def map_power_ratio(value)
{ 'power_ratio' => value.to_f.round }
end

def map_application_version(value)
{ 'application_version' => value }
end
end
6 changes: 3 additions & 3 deletions test/cassettes/influx_success.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/loop_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ def test_start
VCR.use_cassette('influx_success') { Loop.start(config:, max_count: 1) }
end

assert_match(/{"bat_fuel_charge"=>80/, out)
assert_match(/{"bat_charge_current"=>80/, out)
end
end
97 changes: 82 additions & 15 deletions test/mapper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@
MQTT_TOPIC_BAT_VOLTAGE: 'senec/0/ENERGY/GUI_BAT_DATA_VOLTAGE',
MQTT_TOPIC_CASE_TEMP: 'senec/0/TEMPMEASURE/CASE_TEMP',
MQTT_TOPIC_CURRENT_STATE: 'senec/0/ENERGY/STAT_STATE_Text',
MQTT_TOPIC_CURRENT_STATE_CODE: 'senec/0/ENERGY/STAT_STATE',
MQTT_TOPIC_APPLICATION_VERSION: 'senec/0/WIZARD/APPLICATION_VERSION',
MQTT_TOPIC_MPP1_POWER: 'senec/0/PV1/MPP_POWER/0',
MQTT_TOPIC_MPP2_POWER: 'senec/0/PV1/MPP_POWER/1',
MQTT_TOPIC_MPP3_POWER: 'senec/0/PV1/MPP_POWER/2',
MQTT_TOPIC_INVERTER_POWER: 'senec/0/ENERGY/GUI_INVERTER_POWER',
MQTT_TOPIC_WALLBOX_CHARGE_POWER: 'senec/0/WALLBOX/APPARENT_CHARGING_POWER/0',
MQTT_TOPIC_WALLBOX_CHARGE_POWER0: 'senec/0/WALLBOX/APPARENT_CHARGING_POWER/0',
MQTT_TOPIC_WALLBOX_CHARGE_POWER1: 'senec/0/WALLBOX/APPARENT_CHARGING_POWER/1',
MQTT_TOPIC_WALLBOX_CHARGE_POWER2: 'senec/0/WALLBOX/APPARENT_CHARGING_POWER/2',
MQTT_TOPIC_WALLBOX_CHARGE_POWER3: 'senec/0/WALLBOX/APPARENT_CHARGING_POWER/3',
MQTT_TOPIC_POWER_RATIO: 'senec/0/PV1/POWER_RATIO',
}.freeze

class MapperTest < Minitest::Test
Expand All @@ -40,21 +47,31 @@ def mapper(config: nil)
Mapper.new(config: config || default_config)
end

EXPECTED_TOPICS = %w[
senec/0/ENERGY/GUI_BAT_DATA_CURRENT
senec/0/ENERGY/GUI_BAT_DATA_FUEL_CHARGE
senec/0/ENERGY/GUI_BAT_DATA_POWER
senec/0/ENERGY/GUI_BAT_DATA_VOLTAGE
senec/0/ENERGY/GUI_GRID_POW
senec/0/ENERGY/GUI_HOUSE_POW
senec/0/ENERGY/GUI_INVERTER_POWER
senec/0/ENERGY/STAT_STATE
senec/0/ENERGY/STAT_STATE_Text
senec/0/PV1/MPP_POWER/0
senec/0/PV1/MPP_POWER/1
senec/0/PV1/MPP_POWER/2
senec/0/PV1/POWER_RATIO
senec/0/TEMPMEASURE/CASE_TEMP
senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
senec/0/WALLBOX/APPARENT_CHARGING_POWER/1
senec/0/WALLBOX/APPARENT_CHARGING_POWER/2
senec/0/WALLBOX/APPARENT_CHARGING_POWER/3
senec/0/WIZARD/APPLICATION_VERSION
].freeze

def test_topics
assert_equal %w[
senec/0/ENERGY/GUI_BAT_DATA_FUEL_CHARGE
senec/0/ENERGY/GUI_BAT_DATA_POWER
senec/0/ENERGY/GUI_GRID_POW
senec/0/ENERGY/GUI_HOUSE_POW
senec/0/ENERGY/GUI_INVERTER_POWER
senec/0/ENERGY/STAT_STATE_Text
senec/0/PV1/MPP_POWER/0
senec/0/PV1/MPP_POWER/1
senec/0/PV1/MPP_POWER/2
senec/0/TEMPMEASURE/CASE_TEMP
senec/0/WALLBOX/APPARENT_CHARGING_POWER/0
],
mapper.topics
assert_equal EXPECTED_TOPICS, mapper.topics
end

def test_call_with_inverter_power
Expand Down Expand Up @@ -93,10 +110,42 @@ def test_call_with_bat_fuel_charge
assert_equal({ 'bat_fuel_charge' => 123.5 }, hash)
end

def test_call_with_wallbox_charge_power
def test_call_with_bat_charge_current
hash = mapper.call('senec/0/ENERGY/GUI_BAT_DATA_CURRENT', '1.612')

assert_equal({ 'bat_charge_current' => 1.612 }, hash)
end

def test_call_with_bat_voltage
hash = mapper.call('senec/0/ENERGY/GUI_BAT_DATA_VOLTAGE', '54.2')

assert_equal({ 'bat_voltage' => 54.2 }, hash)
end

def test_call_with_wallbox_charge_power0
hash = mapper.call('senec/0/WALLBOX/APPARENT_CHARGING_POWER/0', '123.45')

assert_equal({ 'wallbox_charge_power' => 123 }, hash)
# TODO: Change to 'wallbox_charge_power0' in the next major version.
# Sum up 0..3 needs to be done in SOLECTRUS dashboard
end

def test_call_with_wallbox_charge_power1
hash = mapper.call('senec/0/WALLBOX/APPARENT_CHARGING_POWER/1', '123.45')

assert_equal({ 'wallbox_charge_power1' => 123 }, hash)
end

def test_call_with_wallbox_charge_power2
hash = mapper.call('senec/0/WALLBOX/APPARENT_CHARGING_POWER/2', '123.45')

assert_equal({ 'wallbox_charge_power2' => 123 }, hash)
end

def test_call_with_wallbox_charge_power3
hash = mapper.call('senec/0/WALLBOX/APPARENT_CHARGING_POWER/3', '123.45')

assert_equal({ 'wallbox_charge_power3' => 123 }, hash)
end

def test_call_with_bat_power_plus
Expand Down Expand Up @@ -144,12 +193,30 @@ def test_call_with_current_state
assert_equal({ 'current_state' => 'LOADING' }, hash)
end

def test_call_with_current_state_code
hash = mapper.call('senec/0/ENERGY/STAT_STATE', '14')

assert_equal({ 'current_state_code' => 14 }, hash)
end

def test_call_with_application_version
hash = mapper.call('senec/0/WIZARD/APPLICATION_VERSION', '826')

assert_equal({ 'application_version' => '826' }, hash)
end

def test_call_with_case_temp
hash = mapper.call('senec/0/TEMPMEASURE/CASE_TEMP', '35.2')

assert_equal({ 'case_temp' => 35.2 }, hash)
end

def test_call_with_power_ratio
hash = mapper.call('senec/0/PV1/POWER_RATIO', '70')

assert_equal({ 'power_ratio' => 70 }, hash)
end

def test_call_with_unknown_topic
assert_raises RuntimeError do
mapper.call('this/is/an/unknown/topic', 'foo!')
Expand Down

0 comments on commit 744d8d9

Please sign in to comment.