Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.

Commit 1f45683

Browse files
committed
Adapt for Sungrow CSV files
1 parent ba6f256 commit 1f45683

19 files changed

+640
-403
lines changed

.env.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ INFLUX_PORT=8086
55
INFLUX_TOKEN_WRITE=my-token
66
INFLUX_ORG=my-org
77
INFLUX_BUCKET=my-bucket
8+
INFLUX_MEASUREMENT=my-measurement
89

910
# Folder with CSV test data
1011
IMPORT_FOLDER=./spec/data

.github/workflows/push.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
with:
5151
# list of Docker images to use as base name for tags
5252
images: |
53-
ghcr.io/solectrus/senec-importer
53+
ghcr.io/solectrus/sungrow-importer
5454
# generate Docker tags based on the following events/attributes
5555
tags: |
5656
type=ref,event=branch

.rubocop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ Metrics/MethodLength:
3535
Max: 20
3636

3737
Metrics/AbcSize:
38-
Max: 20
38+
Max: 22

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
FROM ruby:3.2.2-alpine AS Builder
22
RUN apk add --no-cache build-base
33

4-
WORKDIR /senec-importer
4+
WORKDIR /sungrow-importer
55

6-
COPY Gemfile* /senec-importer/
6+
COPY Gemfile* /sungrow-importer/
77
RUN bundle config --local frozen 1 && \
88
bundle config --local without 'development test' && \
99
bundle install -j4 --retry 3 && \
@@ -25,9 +25,9 @@ ENV VERSION ${VERSION}
2525
ARG REVISION
2626
ENV REVISION ${REVISION}
2727

28-
WORKDIR /senec-importer
28+
WORKDIR /sungrow-importer
2929

3030
COPY --from=Builder /usr/local/bundle/ /usr/local/bundle/
31-
COPY . /senec-importer/
31+
COPY . /sungrow-importer/
3232

3333
ENTRYPOINT bundle exec app/main.rb

README.md

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
[![Continuous integration](https://github.com/solectrus/senec-importer/actions/workflows/push.yml/badge.svg)](https://github.com/solectrus/senec-importer/actions/workflows/push.yml)
2-
[![wakatime](https://wakatime.com/badge/user/697af4f5-617a-446d-ba58-407e7f3e0243/project/0fd4e23c-13b0-43a6-bfe0-2f235cbe9785.svg)](https://wakatime.com/badge/user/697af4f5-617a-446d-ba58-407e7f3e0243/project/0fd4e23c-13b0-43a6-bfe0-2f235cbe9785)
1+
[![Continuous integration](https://github.com/solectrus/sungrow-importer/actions/workflows/push.yml/badge.svg)](https://github.com/solectrus/sungrow-importer/actions/workflows/push.yml)
2+
[![wakatime](https://wakatime.com/badge/user/697af4f5-617a-446d-ba58-407e7f3e0243/project/f8429171-106d-4ef2-877c-b5fd73495e6e.svg)](https://wakatime.com/badge/user/697af4f5-617a-446d-ba58-407e7f3e0243/project/f8429171-106d-4ef2-877c-b5fd73495e6e)
33

4-
# SENEC importer
4+
# Sungrow importer
55

6-
Import CSV data downloaded from mein-senec.de and push it to InfluxDB.
6+
Import CSV data downloaded from portaleu.isolarcloud.com and push it to InfluxDB.
77

88
## Requirements
99

1010
- SOLECTRUS installed and running
11-
- CSV files downloaded from mein-senec.de
11+
- CSV files downloaded from portaleu.isolarcloud.com
1212

1313
## Usage
1414

@@ -22,7 +22,7 @@ docker run -it --rm \
2222
--env-file .env \
2323
--mount type=bind,source="$PWD/csv",target=/data,readonly \
2424
--network=solectrus_default \
25-
ghcr.io/solectrus/senec-importer
25+
ghcr.io/solectrus/sungrow-importer
2626
```
2727

2828
(Name of the network may vary, see `docker network ls`)
@@ -50,29 +50,15 @@ Second note: Check the `.env` variable `INSTALLATION_DATE`. This must be set to
5050
| `INFLUX_TOKEN_WRITE` or `INFLUX_TOKEN` | Token for InfluxDB (requires write permissions) | |
5151
| `INFLUX_ORG` | Organization for InfluxDB | |
5252
| `INFLUX_BUCKET` | Bucket for InfluxDB | |
53+
| `INFLUX_MEASUREMENT` | Measurement for InfluxDB | |
5354
| `INFLUX_OPEN_TIMEOUT` | Timeout for InfluxDB connection (in seconds) | `30` |
5455
| `INFLUX_READ_TIMEOUT` | Timeout for InfluxDB read (in seconds) | `30` |
5556
| `INFLUX_WRITE_TIMEOUT` | Timeout for InfluxDB write (in seconds) | `30` |
5657
| `IMPORT_FOLDER` | Folder where CSV files are located | `/data` |
5758
| `IMPORT_PAUSE` | Pause after each imported file (in seconds) | `0` |
5859

59-
## Dealing with missing wallbox measurements
60-
61-
The CSV data from mein-senec.de is not complete, there are no measurements for the wallbox. To get around this, wallbox charges are **estimated** using the following formula:
62-
63-
```
64-
wallbox_charge_power = inverter_power (Stromerzeugung)
65-
+ grid_power_plus (Netzbezug)
66-
+ bat_power_minus (Akkuentnahme)
67-
- grid_power_minus (Netzeinspeisung)
68-
- house_power (Stromverbrauch)
69-
- bat_power_plus (Akkubeladung)
70-
```
71-
72-
Please note that this method appears to be ineffective for processing CSV files that were created from July 2022 onwards. This is because wallbox charges are now being included in the overall house consumption since that time. Therefore, it seems that there is currently no way to import wallbox measurements.
73-
74-
The [senec-collector](https://github.com/solectrus/senec-collector) does not have this problem, as it obtains the wallbox measurements directly.
75-
7660
## License
7761

78-
Copyright (c) 2020-2023 Georg Ledermann, released under the MIT License
62+
Copyright (c) 2023 Georg Ledermann, released under the MIT License
63+
64+
Based on some work by [Rainer Drexler](https://github.com/holiday-sunrise/)

app/config.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
:influx_token,
99
:influx_org,
1010
:influx_bucket,
11+
:influx_measurement,
1112
:influx_open_timeout,
1213
:influx_read_timeout,
1314
:influx_write_timeout,
@@ -35,6 +36,7 @@ def self.from_env(options = {})
3536
ENV.fetch('INFLUX_TOKEN_WRITE', nil) || ENV.fetch('INFLUX_TOKEN'),
3637
influx_org: ENV.fetch('INFLUX_ORG'),
3738
influx_bucket: ENV.fetch('INFLUX_BUCKET'),
39+
influx_measurement: ENV.fetch('INFLUX_MEASUREMENT'),
3840
influx_open_timeout: ENV.fetch('INFLUX_OPEN_TIMEOUT', 30).to_i,
3941
influx_read_timeout: ENV.fetch('INFLUX_READ_TIMEOUT', 30).to_i,
4042
influx_write_timeout: ENV.fetch('INFLUX_WRITE_TIMEOUT', 30).to_i,

app/flux_writer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def point(record)
3232
end
3333

3434
def influx_measurement
35-
'SENEC'
35+
config.influx_measurement
3636
end
3737

3838
def influx_client

app/import.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ def process(filename)
3232
count = 0
3333
records =
3434
CSV
35-
.parse(File.read(filename), headers: true, col_sep: ';')
35+
.parse(File.read(filename), headers: true, col_sep: ',')
3636
.map do |row|
3737
count += 1
3838

39-
SolectrusRecord.new(row).to_h
39+
SolectrusRecord.new(row, measurement: config.influx_measurement).to_h
4040
end
4141

4242
return unless count.positive?

app/main.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
# Flush output immediately
88
$stdout.sync = true
99

10-
puts 'SENEC CSV importer for SOLECTRUS, ' \
10+
puts 'Sungrow CSV importer for SOLECTRUS, ' \
1111
"Version #{ENV.fetch('VERSION', '<unknown>')}, " \
1212
"built at #{ENV.fetch('BUILDTIME', '<unknown>')}"
13-
puts 'https://github.com/solectrus/senec-importer'
13+
puts 'https://github.com/solectrus/sungrow-importer'
1414
puts 'Copyright (c) 2020-2023 Georg Ledermann, released under the MIT License'
1515
puts "\n"
1616

app/solectrus_record.rb

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
ENV['TZ'] = 'CET'
22

33
class SolectrusRecord
4-
def initialize(row)
4+
def initialize(row, measurement:)
55
@row = row
6+
@measurement = measurement
67
end
78

8-
attr_reader :row
9+
attr_reader :row, :measurement
910

1011
def to_h
11-
{ name: 'SENEC', time:, fields: }
12+
{ name: measurement, time:, fields: }
1213
end
1314

1415
private
1516

1617
def time
17-
parse_time(row, 'Uhrzeit')
18+
parse_time(row, 'Zeit')
1819
end
1920

2021
def fields
@@ -24,84 +25,46 @@ def fields
2425
bat_power_plus:,
2526
bat_power_minus:,
2627
bat_fuel_charge: nil,
27-
bat_charge_current:,
28-
bat_voltage:,
2928
grid_power_plus:,
3029
grid_power_minus:,
31-
# There is no data for the wallbox, but we can estimate it
32-
wallbox_charge_power: estimated_wallbox_charge_power,
3330
}
3431
end
3532

3633
def inverter_power
37-
@inverter_power ||=
38-
parse_kw(row, 'Stromerzeugung [kW]', 'Stromerzeugung [kWh]')
34+
@inverter_power ||= parse_kw(row, 'PV-Ertrag(W)')
3935
end
4036

4137
def house_power
42-
@house_power ||=
43-
parse_kw(row, 'Stromverbrauch [kW]', 'Stromverbrauch [kWh]')
38+
@house_power ||= parse_kw(row, 'Gesamtverbrauch(W)')
4439
end
4540

4641
def bat_power_plus
47-
@bat_power_plus ||=
48-
parse_kw(
49-
row,
50-
'Akkubeladung [kW]',
51-
'Akku-Beladung [kW]',
52-
'Akku-Beladung [kWh]',
53-
)
42+
@bat_power_plus ||= bat_power.negative? ? -bat_power : 0.0
5443
end
5544

5645
def bat_power_minus
57-
# The CSV file format has changed over time, so two different column names are possible
58-
@bat_power_minus ||=
59-
parse_kw(
60-
row,
61-
'Akkuentnahme [kW]',
62-
'Akku-Entnahme [kW]',
63-
'Akku-Entnahme [kWh]',
64-
)
46+
@bat_power_minus ||= bat_power.positive? ? bat_power : 0.0
6547
end
6648

67-
def bat_charge_current
68-
@bat_charge_current ||= parse_a(row, 'Akku Stromstärke [A]')
69-
end
70-
71-
def bat_voltage
72-
@bat_voltage ||= parse_v(row, 'Akku Spannung [V]')
49+
def bat_power
50+
@bat_power ||= parse_kw(row, 'Batterie(W)')
7351
end
7452

7553
def grid_power_plus
76-
@grid_power_plus ||= parse_kw(row, 'Netzbezug [kW]', 'Netzbezug [kWh]')
54+
@grid_power_plus ||= grid_power.positive? ? grid_power : 0.0
7755
end
7856

7957
def grid_power_minus
80-
@grid_power_minus ||=
81-
parse_kw(row, 'Netzeinspeisung [kW]', 'Netzeinspeisung [kWh]')
58+
@grid_power_minus ||= grid_power.negative? ? -grid_power : 0.0
8259
end
8360

84-
def estimated_wallbox_charge_power
85-
incoming = inverter_power + grid_power_plus + bat_power_minus
86-
outgoing = grid_power_minus + house_power + bat_power_plus
87-
diff = incoming - outgoing
88-
89-
diff < 50 ? 0 : diff
61+
def grid_power
62+
@grid_power ||= parse_kw(row, 'Netz(W)')
9063
end
9164

9265
# KiloWatt
9366
def parse_kw(row, *columns)
94-
(cell(row, *columns).sub(',', '.').to_f * 1_000).round
95-
end
96-
97-
# Ampere
98-
def parse_a(row, *columns)
99-
cell(row, *columns).sub(',', '.').to_f
100-
end
101-
102-
# Volt
103-
def parse_v(row, *columns)
104-
cell(row, *columns).sub(',', '.').to_f
67+
cell(row, *columns).to_f
10568
end
10669

10770
# Time

0 commit comments

Comments
 (0)