Skip to content

Commit

Permalink
Merge pull request #10 from salberin/add-ci
Browse files Browse the repository at this point in the history
Add and enforce standard linting and formatting
  • Loading branch information
peplin authored Jan 12, 2024
2 parents 825a746 + b4902f4 commit 8303c3c
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 60 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -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/[email protected]
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,4 @@ venv.bak/
/site

# mypy
.mypy_cache/
.mypy_cache/
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -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.
limitations under the License.
2 changes: 1 addition & 1 deletion readme.MD → README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ In addition, you can specify additional flags to customize the tool:
* `--unitid <id>` specifies the ModBus ID used by the inverter (default 1)
* `--port <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 <x>` or `--period <x>` record data every <x> seconds (default 5)
* `-p <x>` or `--period <x>` record data every <x> seconds (default 5)
153 changes: 97 additions & 56 deletions solaredge.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
Expand All @@ -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)
)

0 comments on commit 8303c3c

Please sign in to comment.