Skip to content

Commit c19e436

Browse files
committed
Merge branch 'release/0.14.0'
2 parents 33023b1 + 9a58e9c commit c19e436

17 files changed

Lines changed: 174 additions & 104 deletions

.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ compose.yml
77
spec
88
Rakefile
99
Dockerfile
10+
.dockerignore
11+
.rspec
12+
.rubocop.yml
13+
.vscode
14+
Guardfile

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ SENEC_SYSTEM_ID=1236456
2525
# Interval in seconds to get data from SENEC cloud (minimum: 30 seconds)
2626
SENEC_INTERVAL=60
2727

28+
# Optional: Disable specific fields you do not want to send to InfluxDB.
29+
# This can be useful if you are tracking them from another source.
30+
# Comma separated list of fields, no whitespace. Example:
31+
# SENEC_IGNORE=wallbox_charge_power,grid_power_minus
32+
2833
# Credentials of your InfluxDB installation
2934
INFLUX_HOST=influxdb.example.com
3035
INFLUX_SCHEMA=https

.github/workflows/automerge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
steps:
1313
- name: Dependabot metadata
1414
id: metadata
15-
uses: dependabot/fetch-metadata@v1.6.0
15+
uses: dependabot/fetch-metadata@v2.0.0
1616
with:
1717
github-token: '${{ secrets.PAT }}'
1818

.github/workflows/push.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ jobs:
9191
with:
9292
context: .
9393
platforms: linux/amd64,linux/arm64,linux/arm/v7
94+
provenance: false
9495
push: true
9596
tags: ${{ steps.meta.outputs.tags }}
9697
labels: ${{ steps.meta.outputs.labels }}

Dockerfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ ENV REVISION ${REVISION}
2929

3030
WORKDIR /senec-collector
3131

32-
# The heartbeat is written at least every 60 seconds, so the container
33-
# is considered healthy if the last heartbeat was less than 70 seconds ago.
34-
HEALTHCHECK CMD test $(expr $(date +%s) - $(cat /tmp/heartbeat.txt)) -lt 70 || exit 1
35-
3632
COPY --from=Builder /usr/local/bundle/ /usr/local/bundle/
3733
COPY . /senec-collector/
3834

Gemfile.lock

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ GEM
33
specs:
44
addressable (2.8.6)
55
public_suffix (>= 2.0.2, < 6.0)
6-
amazing_print (1.5.0)
6+
amazing_print (1.6.0)
77
ast (2.4.2)
88
base64 (0.2.0)
9-
bigdecimal (3.1.6)
9+
bigdecimal (3.1.7)
1010
coderay (1.1.3)
1111
connection_pool (2.4.1)
1212
crack (1.0.0)
1313
bigdecimal
1414
rexml
15-
csv (3.2.8)
15+
csv (3.3.0)
1616
diff-lcs (1.5.1)
1717
docile (1.4.0)
1818
dotenv (3.1.0)
@@ -42,14 +42,14 @@ GEM
4242
guard-compat (~> 1.1)
4343
rspec (>= 2.99.0, < 4.0)
4444
hashdiff (1.1.0)
45-
influxdb-client (3.0.0)
46-
json (2.7.1)
45+
influxdb-client (3.1.0)
46+
json (2.7.2)
4747
language_server-protocol (3.17.0.3)
4848
listen (3.9.0)
4949
rb-fsevent (~> 0.10, >= 0.10.3)
5050
rb-inotify (~> 0.9, >= 0.9.10)
5151
lumberjack (1.2.10)
52-
method_source (1.0.0)
52+
method_source (1.1.0)
5353
nenv (0.3.0)
5454
net-http (0.4.1)
5555
uri
@@ -64,14 +64,13 @@ GEM
6464
parser (3.3.0.5)
6565
ast (~> 2.4.1)
6666
racc
67-
prism (0.24.0)
6867
pry (0.14.2)
6968
coderay (~> 1.1)
7069
method_source (~> 1.0)
71-
public_suffix (5.0.4)
70+
public_suffix (5.0.5)
7271
racc (1.7.3)
7372
rainbow (3.1.1)
74-
rake (13.1.0)
73+
rake (13.2.1)
7574
rb-fsevent (0.11.2)
7675
rb-inotify (0.10.1)
7776
ffi (~> 1.0)
@@ -90,33 +89,35 @@ GEM
9089
diff-lcs (>= 1.2.0, < 2.0)
9190
rspec-support (~> 3.13.0)
9291
rspec-support (3.13.1)
93-
rubocop (1.61.0)
92+
rubocop (1.63.2)
9493
json (~> 2.3)
9594
language_server-protocol (>= 3.17.0)
9695
parallel (~> 1.10)
9796
parser (>= 3.3.0.2)
9897
rainbow (>= 2.2.2, < 4.0)
9998
regexp_parser (>= 1.8, < 3.0)
10099
rexml (>= 3.2.5, < 4.0)
101-
rubocop-ast (>= 1.30.0, < 2.0)
100+
rubocop-ast (>= 1.31.1, < 2.0)
102101
ruby-progressbar (~> 1.7)
103102
unicode-display_width (>= 2.4.0, < 3.0)
104-
rubocop-ast (1.31.0)
103+
rubocop-ast (1.31.2)
105104
parser (>= 3.3.0.4)
106-
prism (>= 0.24.0)
107105
rubocop-capybara (2.20.0)
108106
rubocop (~> 1.41)
109107
rubocop-factory_bot (2.25.1)
110108
rubocop (~> 1.41)
111-
rubocop-performance (1.20.2)
109+
rubocop-performance (1.21.0)
112110
rubocop (>= 1.48.1, < 2.0)
113-
rubocop-ast (>= 1.30.0, < 2.0)
111+
rubocop-ast (>= 1.31.1, < 2.0)
114112
rubocop-rake (0.6.0)
115113
rubocop (~> 1.0)
116-
rubocop-rspec (2.26.1)
114+
rubocop-rspec (2.29.1)
117115
rubocop (~> 1.40)
118116
rubocop-capybara (~> 2.17)
119117
rubocop-factory_bot (~> 2.22)
118+
rubocop-rspec_rails (~> 2.28)
119+
rubocop-rspec_rails (2.28.3)
120+
rubocop (~> 1.40)
120121
ruby-progressbar (1.13.0)
121122
senec (0.15.0)
122123
faraday
@@ -161,4 +162,4 @@ DEPENDENCIES
161162
webmock
162163

163164
BUNDLED WITH
164-
2.5.6
165+
2.5.9

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
# SENEC collector
77

8-
Collect data from SENEC photovoltaics and push it to InfluxDB 2
8+
Collect data from SENEC Solar Battery Storage System and push it to InfluxDB 2
99

1010
Allows accessing a local SENEC device (V2 + V3) as well as the SENEC cloud (V4).
1111

@@ -15,8 +15,9 @@ Linux machine with Docker installed
1515

1616
Tested on:
1717

18-
- Raspberry Pi 4 Model B on Raspbian GNU/Linux 10 (buster) and 11 (bullseye)
19-
- Synology NAS DS220+ on DSM 7.1
18+
- Raspberry Pi 4 Model B on Raspbian GNU/Linux 10 (buster) and 11 (bullseye) and 12 (bookworm)
19+
- Synology NAS DS220+ on DSM 7.1+
20+
- ... and lots of other Linux machines - if it runs Docker, it should work
2021

2122
## Getting started
2223

app.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
logger.info "Pushing to InfluxDB at #{config.influx_url}, " \
2929
"bucket #{config.influx_bucket}, " \
3030
"measurement #{config.influx_measurement}"
31+
logger.info "Ignoring: #{config.senec_ignore.join(', ')}" if config.senec_ignore.any?
3132
logger.info "\n"
3233

3334
Loop.start(config:)

lib/cloud_adapter.rb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def technical_data
8484
Senec::Cloud::TechnicalData[connection].find(system_id)
8585
end
8686

87-
def record_hash
87+
def raw_record_hash
8888
{
8989
current_state:,
9090
current_state_ok:,
@@ -104,6 +104,10 @@ def record_hash
104104
}.compact
105105
end
106106

107+
def record_hash
108+
raw_record_hash.except(*config.senec_ignore)
109+
end
110+
107111
def dashboard_record
108112
@dashboard_record ||= dashboard.data
109113
end
@@ -192,11 +196,14 @@ def current_state_ok
192196
end
193197

194198
def success_message(record)
195-
"\nGot record ##{record.id} at " \
196-
"#{Time.at(record.measure_time).localtime} " \
197-
"#{record.current_state || 'Unknown state'}, " \
198-
"Inverter #{record.inverter_power} W, House #{record.house_power} W, " \
199-
"Wallbox #{record.wallbox_charge_power} W"
199+
[
200+
"\nGot record ##{record.id} at " \
201+
"#{Time.at(record.measure_time).localtime}",
202+
record.current_state || 'Unknown state',
203+
("Inverter #{record.inverter_power} W" unless config.excludes?(:inverter_power)),
204+
("House #{record.house_power} W" unless config.excludes?(:house_power)),
205+
("Wallbox #{record.wallbox_charge_power} W" unless config.excludes?(:wallbox_charge_power)),
206+
].compact.join(', ')
200207
end
201208

202209
def failure_message(error)

lib/config.rb

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
senec_password
1414
senec_token
1515
senec_system_id
16+
senec_ignore
1617
influx_schema
1718
influx_host
1819
influx_port
@@ -26,6 +27,7 @@
2627
senec_adapter: :local,
2728
senec_schema: :https,
2829
senec_language: :de,
30+
senec_ignore: [],
2931
influx_schema: :http,
3032
influx_port: 8086,
3133
influx_measurement: 'SENEC',
@@ -36,30 +38,25 @@
3638
def initialize(*options)
3739
super
3840

39-
set_defaults_and_types
40-
validate!
41-
end
42-
43-
def set_defaults_and_types
44-
convert_types
41+
set_types
4542
set_defaults
46-
limit_interval
43+
limit_senec_interval
44+
validate!
4745
end
4846

49-
def convert_types
50-
# Strip blanks
47+
def set_types
5148
KEYS.each do |key|
5249
self[key] = self[key].presence
53-
end
54-
55-
# Symbols
56-
%i[senec_adapter senec_schema senec_language influx_schema].each do |key|
57-
self[key] = self[key]&.to_sym
58-
end
59-
60-
# Integer
61-
%i[senec_interval influx_port].each do |key|
62-
self[key] = self[key]&.to_i
50+
next unless self[key]
51+
52+
case key
53+
when :senec_adapter, :senec_schema, :senec_language, :influx_schema
54+
self[key] = self[key].to_sym
55+
when :senec_interval, :influx_port
56+
self[key] = self[key].to_i
57+
when :senec_ignore
58+
self[key] = self[key].split(',').map(&:to_sym)
59+
end
6360
end
6461
end
6562

@@ -71,7 +68,7 @@ def set_defaults
7168
self[:senec_interval] ||= senec_adapter == :local ? 5 : 60
7269
end
7370

74-
def limit_interval
71+
def limit_senec_interval
7572
minimum = case senec_adapter
7673
when :local
7774
# Be careful with your local SENEC device, do not flood it with queries.
@@ -97,7 +94,8 @@ def validate!
9794
end
9895

9996
validate_influx_settings!
100-
validate_interval!(senec_interval)
97+
validate_senec_interval!
98+
validate_senec_ignore!
10199
end
102100

103101
def influx_url
@@ -118,6 +116,10 @@ def adapter
118116
end
119117
end
120118

119+
def excludes?(key)
120+
senec_ignore.include?(key)
121+
end
122+
121123
attr_writer :logger
122124

123125
def logger
@@ -130,8 +132,8 @@ def validate_senec_adapter!
130132
%i[local cloud].include?(senec_adapter) || throw("SENEC_ADAPTER is invalid: #{senec_adapter}")
131133
end
132134

133-
def validate_interval!(interval)
134-
(interval.is_a?(Integer) && interval.positive?) || throw("SENEC_INTERVAL is invalid: #{interval}")
135+
def validate_senec_interval!
136+
(senec_interval.is_a?(Integer) && senec_interval.positive?) || throw("SENEC_INTERVAL is invalid: #{senec_interval}")
135137
end
136138

137139
def validate_senec_credentials!
@@ -142,6 +144,12 @@ def validate_senec_credentials!
142144
senec_username.include?('@') || throw('SENEC_USERNAME is invalid')
143145
end
144146

147+
def validate_senec_ignore!
148+
senec_ignore.all? do |key|
149+
SolectrusRecord::KEYS.include?(key) || throw("SENEC_IGNORE contains unknown field: #{key}")
150+
end
151+
end
152+
145153
def validate_influx_settings!
146154
%i[
147155
influx_schema
@@ -166,24 +174,9 @@ def validate_url!(url)
166174

167175
def self.from_env(options = {})
168176
new(
169-
{
170-
senec_adapter: ENV.fetch('SENEC_ADAPTER', nil),
171-
senec_host: ENV.fetch('SENEC_HOST', nil),
172-
senec_schema: ENV.fetch('SENEC_SCHEMA', nil),
173-
senec_interval: ENV.fetch('SENEC_INTERVAL', nil),
174-
senec_language: ENV.fetch('SENEC_LANGUAGE', nil),
175-
senec_username: ENV.fetch('SENEC_USERNAME', nil),
176-
senec_password: ENV.fetch('SENEC_PASSWORD', nil),
177-
senec_token: ENV.fetch('SENEC_TOKEN', nil),
178-
senec_system_id: ENV.fetch('SENEC_SYSTEM_ID', nil),
179-
influx_host: ENV.fetch('INFLUX_HOST'),
180-
influx_schema: ENV.fetch('INFLUX_SCHEMA', nil),
181-
influx_port: ENV.fetch('INFLUX_PORT', nil),
182-
influx_token: ENV.fetch('INFLUX_TOKEN'),
183-
influx_org: ENV.fetch('INFLUX_ORG'),
184-
influx_bucket: ENV.fetch('INFLUX_BUCKET', nil),
185-
influx_measurement: ENV.fetch('INFLUX_MEASUREMENT', nil),
186-
}.merge(options),
177+
KEYS.to_h do |key|
178+
[key, ENV.fetch(key.to_s.upcase, nil)]
179+
end.merge(options),
187180
)
188181
end
189182
end

0 commit comments

Comments
 (0)