Skip to content

Commit 6c0d834

Browse files
dsteinkopfclaude
andcommitted
feat: make OpenDTU poll interval and HTTP retries configurable
The mainloop is single threaded and fetches every inverter separately and synchronously, so poll rate and retry count decide how long dbus method calls on the service stay unanswered: worst case = inverters x tries x HTTPTimeout. With five inverters, three tries and HTTPTimeout=1.5 that is 27.5s. A DTU that turns sluggish therefore blocks the mainloop long enough for GetValue callers to run into org.freedesktop.DBus.Error.NoReply, while svstat and the log still look healthy because values keep being published. Both values were hardcoded. They now come from config.ini as OpenDTUPollingIntervall and MaxFetchTries, defaulting to the previous 5000ms and 3 tries, so existing setups are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent be59048 commit 6c0d834

3 files changed

Lines changed: 20 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ Within the project there is a file `/data/dbus-opendtu/config.ini`. Most importa
123123
| NumberOfInvertersToQuery | Number of Inverters to query. Set a value larger than "0" when not all inverters should be considered. \*1 |
124124
| useYieldDay | send YieldDay instead of YieldTotal. Set this to 1 to prevent VRM from adding the total value to the history on one day. E.g. if you don't start using the inverter at 0. |
125125
| ESP8266PollingIntervall | For ESP8266 reduce polling intervall to reduce load, default 10000ms |
126+
| OpenDTUPollingIntervall | How often an OpenDTU is polled, in ms. Raise this if the DTU gets slow with many inverters. Default: 5000ms |
127+
| MaxFetchTries | How often a failed HTTP call is attempted before the value is dropped. Every attempt blocks the mainloop for up to HTTPTimeout, so lowering this keeps the dbus service responsive while the DTU is slow. Default: 3 |
126128
| Logging | Valid options for log level: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET, to keep logfile small use ERROR or CRITICAL |
127129
| MaxAgeTsLastSuccess | Maximum accepted age of ts_last_success in Ahoy status message. If ts_last_success is older than this number of seconds, values are not used. Set this to < 0 to disable this check. |
128130
| DryRun | Set this to a value different to "0" to prevent values from being sent. Use this for debugging or experiments. |

config.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ useYieldDay=0
1919
#For ESP8266 reduce polling intervall to reduce load
2020
ESP8266PollingIntervall=10000
2121

22+
#How often an OpenDTU is polled, in ms. Raise this if the DTU gets slow with many inverters.
23+
OpenDTUPollingIntervall=5000
24+
25+
#How often a failed HTTP call is attempted before the value is dropped.
26+
#Every attempt blocks the mainloop for up to HTTPTimeout, so lowering this keeps the dbus
27+
#service responsive while the DTU is slow, at the price of losing a reading now and then.
28+
MaxFetchTries=3
29+
2230
#Possible Options for Log Level: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET
2331
#To keep current.log small use ERROR
2432
Logging=ERROR

dbus_service.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@ def _read_config_dtu(self, actual_inverter):
220220

221221
self.dry_run = is_true(get_default_config(config, "DryRun", False))
222222
self.pollinginterval = int(get_config_value(config, "ESP8266PollingIntervall", "DEFAULT", "", 10000))
223+
# The mainloop is single threaded and fetches every inverter separately and
224+
# synchronously, so poll rate and retry count decide how long dbus method calls on this
225+
# service stay unanswered: worst case = inverters x tries x HTTPTimeout. With five
226+
# inverters, three tries and a 1.5s timeout that is 27.5s - long enough for GetValue
227+
# callers to run into org.freedesktop.DBus.Error.NoReply while the DTU is slow.
228+
self.opendtu_polling_interval = int(get_config_value(config, "OpenDTUPollingIntervall", "DEFAULT", "", 5000))
229+
self.max_fetch_tries = int(get_config_value(config, "MaxFetchTries", "DEFAULT", "", 3))
223230
self.meter_data = 0
224231
self.httptimeout = get_default_config(config, "HTTPTimeout", 2.5)
225232
self._load_error_handling_config(config)
@@ -352,7 +359,7 @@ def _get_polling_interval(self):
352359
polling_interval = 5000
353360

354361
elif self.dtuvariant == constants.DTUVARIANT_OPENDTU:
355-
polling_interval = 5000
362+
polling_interval = self.opendtu_polling_interval
356363

357364
elif self.dtuvariant == constants.DTUVARIANT_TEMPLATE:
358365
polling_interval = self.pollinginterval
@@ -493,8 +500,8 @@ def fetch_url(self, url, try_number=1):
493500
f"status={json_str.status_code},\nresponse={json_str.text}")
494501
return json
495502
except Exception:
496-
# retry same call up to 3 times
497-
if try_number < 3: # pylint: disable=no-else-return
503+
# retry same call up to MaxFetchTries times
504+
if try_number < self.max_fetch_tries: # pylint: disable=no-else-return
498505
time.sleep(0.5)
499506
return self.fetch_url(url, try_number + 1)
500507
else:

0 commit comments

Comments
 (0)