From 6813bee0eb8ed8b32aee1da133af38126dfa2a07 Mon Sep 17 00:00:00 2001 From: Christopher Peplin Date: Fri, 12 Jan 2024 18:33:33 -0500 Subject: [PATCH 1/2] Add pre-commit hooks and CI workflow to enforce --- .github/workflows/pre-commit.yml | 14 ++++++++++++++ .pre-commit-config.yaml | 15 +++++++++++++++ readme.MD => README.md | 0 3 files changed, 29 insertions(+) create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml rename readme.MD => README.md (100%) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..c2f7e71 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,14 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [main] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..426c396 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,15 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: c4a0b883114b00d8d76b479c820ce7950211c99b # frozen: v4.5.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: ec91a2be3c44d88e1a3960a4937ad6ed3b63464e # frozen: 23.12.1 + hooks: + - id: black + - repo: https://github.com/rhysd/actionlint + rev: ea8102762106cdca9c88829f1295b39a544706f3 # frozen: v1.6.26 + hooks: + - id: actionlint diff --git a/readme.MD b/README.md similarity index 100% rename from readme.MD rename to README.md From b4902f4f21b5a5a52590d1443dc62929048297be Mon Sep 17 00:00:00 2001 From: Christopher Peplin Date: Fri, 12 Jan 2024 18:33:48 -0500 Subject: [PATCH 2/2] Apply formatting/linting from pre-commit --- .gitignore | 2 +- Dockerfile | 1 - LICENSE | 2 +- README.md | 2 +- solaredge.py | 153 ++++++++++++++++++++++++++++++++------------------- 5 files changed, 100 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 92d263d..52975f2 100644 --- a/.gitignore +++ b/.gitignore @@ -165,4 +165,4 @@ venv.bak/ /site # mypy -.mypy_cache/ \ No newline at end of file +.mypy_cache/ diff --git a/Dockerfile b/Dockerfile index bd0d110..3537f91 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,4 +14,3 @@ RUN pip3 install -r /requirements.txt ADD solaredge.py / CMD python3 /solaredge.py --influxdb $INFLUXDB --influxport $INFLUXPORT --port $INVERTERPORT --unitid $UNITID $INVERTER - diff --git a/LICENSE b/LICENSE index f49a4e1..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/README.md b/README.md index fbe3187..67ffa27 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ In addition, you can specify additional flags to customize the tool: * `--unitid ` specifies the ModBus ID used by the inverter (default 1) * `--port ` specifies the ModBus TCP port to connect to (default 502) * `-d` or `--debug` activates debug logging, adding an additional -d includes aioinflux logging -* `-p ` or `--period ` record data every seconds (default 5) \ No newline at end of file +* `-p ` or `--period ` record data every seconds (default 5) diff --git a/solaredge.py b/solaredge.py index 96d737c..2a1663f 100644 --- a/solaredge.py +++ b/solaredge.py @@ -9,25 +9,19 @@ import asyncio from aioinflux import InfluxDBClient, InfluxDBWriteError -datapoint = { - 'measurement': 'SolarEdge', - 'tags': { - 'inverter': '1' - }, - 'fields': {} -} +datapoint = {"measurement": "SolarEdge", "tags": {"inverter": "1"}, "fields": {}} -logger = logging.getLogger('solaredge') +logger = logging.getLogger("solaredge") # Sentinel value to indicate the feature in a register is not implemented NOT_IMPLEMENTED_VALUE = 65535 -async def write_to_influx(dbhost, dbport, period, dbname='solaredge'): +async def write_to_influx(dbhost, dbport, period, dbname="solaredge"): global client def trunc_float(floatval): - return float('%.2f' % floatval) + return float("%.2f" % floatval) def decode_value(data, scalefactor): if data == NOT_IMPLEMENTED_VALUE: @@ -42,10 +36,10 @@ def decode_value(data, scalefactor): solar_client = InfluxDBClient(host=dbhost, port=dbport, db=dbname) await solar_client.create_database(db=dbname) except ClientConnectionError as e: - logger.error(f'Error during connection to InfluxDb {dbhost}: {e}') + logger.error(f"Error during connection to InfluxDb {dbhost}: {e}") return - logger.info('Database opened and initialized') + logger.info("Database opened and initialized") while True: try: reg_block = client.read_holding_registers(40069, 38) @@ -55,87 +49,134 @@ def decode_value(data, scalefactor): reg_block, byteorder=Endian.BIG, wordorder=Endian.BIG ) data.skip_bytes(12) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-10) # Register 40072-40075 - datapoint['fields']['AC Total Current'] = decode_value(data.decode_16bit_uint(), scalefactor) - datapoint['fields']['AC Current phase A'] = decode_value(data.decode_16bit_uint(), scalefactor) - datapoint['fields']['AC Current phase B'] = decode_value(data.decode_16bit_uint(), scalefactor) - datapoint['fields']['AC Current phase C'] = decode_value(data.decode_16bit_uint(), scalefactor) + datapoint["fields"]["AC Total Current"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) + datapoint["fields"]["AC Current phase A"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) + datapoint["fields"]["AC Current phase B"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) + datapoint["fields"]["AC Current phase C"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) data.skip_bytes(14) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-8) # register 40080-40082 - datapoint['fields']['AC Voltage phase A'] = decode_value(data.decode_16bit_uint(), scalefactor) - datapoint['fields']['AC Voltage phase B'] = decode_value(data.decode_16bit_uint(), scalefactor) - datapoint['fields']['AC Voltage phase C'] = decode_value(data.decode_16bit_uint(), scalefactor) + datapoint["fields"]["AC Voltage phase A"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) + datapoint["fields"]["AC Voltage phase B"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) + datapoint["fields"]["AC Voltage phase C"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) data.skip_bytes(4) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-4) # register 40084 - datapoint['fields']['AC Power output'] = decode_value(data.decode_16bit_int(), scalefactor) + datapoint["fields"]["AC Power output"] = decode_value( + data.decode_16bit_int(), scalefactor + ) data.skip_bytes(24) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-6) # register 40094 - datapoint['fields']['AC Lifetimeproduction'] = decode_value(data.decode_32bit_uint(), scalefactor) + datapoint["fields"]["AC Lifetimeproduction"] = decode_value( + data.decode_32bit_uint(), scalefactor + ) data.skip_bytes(2) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-2) # register 40097 - datapoint['fields']['DC Current'] = decode_value(data.decode_16bit_uint() *scalefactor) + datapoint["fields"]["DC Current"] = decode_value( + data.decode_16bit_uint() * scalefactor + ) data.skip_bytes(4) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-4) # register 40099 - datapoint['fields']['DC Voltage'] = decode_value(data.decode_16bit_uint(), scalefactor) + datapoint["fields"]["DC Voltage"] = decode_value( + data.decode_16bit_uint(), scalefactor + ) data.skip_bytes(4) - scalefactor = 10**data.decode_16bit_int() + scalefactor = 10 ** data.decode_16bit_int() data.skip_bytes(-4) # datapoint 40101 - datapoint['fields']['DC Power input'] = decode_value(data.decode_16bit_int(), scalefactor) + datapoint["fields"]["DC Power input"] = decode_value( + data.decode_16bit_int(), scalefactor + ) - datapoint['time'] = str(datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat()) - logger.debug(f'Writing to Influx: {str(datapoint)}') + datapoint["time"] = str( + datetime.datetime.utcnow() + .replace(tzinfo=datetime.timezone.utc) + .isoformat() + ) + logger.debug(f"Writing to Influx: {str(datapoint)}") await solar_client.write(datapoint) else: # Error during data receive if client.last_error() == 2: - logger.error(f'Failed to connect to SolarEdge inverter {client.host()}!') + logger.error( + f"Failed to connect to SolarEdge inverter {client.host()}!" + ) elif client.last_error() == 3 or client.last_error() == 4: - logger.error('Send or receive error!') + logger.error("Send or receive error!") elif client.last_error() == 5: - logger.error('Timeout during send or receive operation!') + logger.error("Timeout during send or receive operation!") except InfluxDBWriteError as e: - logger.error(f'Failed to write to InfluxDb: {e}') + logger.error(f"Failed to write to InfluxDb: {e}") except IOError as e: - logger.error(f'I/O exception during operation: {e}') + logger.error(f"I/O exception during operation: {e}") except Exception as e: - logger.exception(f'Unhandled exception: {e}') + logger.exception(f"Unhandled exception: {e}") await asyncio.sleep(period) -if __name__ == '__main__': + +if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('--influxdb', default='localhost') - parser.add_argument('--influxport', type=int, default=8086) - parser.add_argument('--port', type=int, default=502, help='ModBus TCP port number to use') - parser.add_argument('--unitid', type=int, default=1, help='ModBus unit id to use in communication') - parser.add_argument('solaredge', metavar='SolarEdge IP', help='IP address of the SolarEdge inverter to monitor') - parser.add_argument('--period', '-p', type=int, default=5) - parser.add_argument('--debug', '-d', action='count') + parser.add_argument("--influxdb", default="localhost") + parser.add_argument("--influxport", type=int, default=8086) + parser.add_argument( + "--port", type=int, default=502, help="ModBus TCP port number to use" + ) + parser.add_argument( + "--unitid", type=int, default=1, help="ModBus unit id to use in communication" + ) + parser.add_argument( + "solaredge", + metavar="SolarEdge IP", + help="IP address of the SolarEdge inverter to monitor", + ) + parser.add_argument("--period", "-p", type=int, default=5) + parser.add_argument("--debug", "-d", action="count") args = parser.parse_args() logging.basicConfig() if args.debug and args.debug >= 1: - logging.getLogger('solaredge').setLevel(logging.DEBUG) + logging.getLogger("solaredge").setLevel(logging.DEBUG) if args.debug and args.debug == 2: - logging.getLogger('aioinflux').setLevel(logging.DEBUG) - - print('Starting up solaredge monitoring') - print(f'Connecting to Solaredge inverter {args.solaredge} on port {args.port} using unitid {args.unitid}') - print(f'Writing data to influxDb {args.influxdb} on port {args.influxport} every {args.period} seconds') - client = ModbusClient(args.solaredge, port=args.port, unit_id=args.unitid, auto_open=True) - logger.debug('Running eventloop') - asyncio.get_event_loop().run_until_complete(write_to_influx(args.influxdb, args.influxport, args.period)) + logging.getLogger("aioinflux").setLevel(logging.DEBUG) + + print("Starting up solaredge monitoring") + print( + f"Connecting to Solaredge inverter {args.solaredge} on port {args.port} using unitid {args.unitid}" + ) + print( + f"Writing data to influxDb {args.influxdb} on port {args.influxport} every {args.period} seconds" + ) + client = ModbusClient( + args.solaredge, port=args.port, unit_id=args.unitid, auto_open=True + ) + logger.debug("Running eventloop") + asyncio.get_event_loop().run_until_complete( + write_to_influx(args.influxdb, args.influxport, args.period) + )