#!/usr/bin/env python3

import argparse
import datetime
import prom
import sunspec

metric_data = {
    "dc_voltage_volts": ["gauge", "Input bus voltage"],
    "dc_current_amps": ["gauge", "Input bus current"],
    "dc_power_watts": ["gauge", "Input bus power"],
    "energy_watthours_total": ["counter", "Power meter total"],
    "temperature_celsius": ["gauge", "Temperature"],
    "inverter_status": ["gauge", "Inverter state (1=off, 2=sleep, 4=producing, 7=fault)"],
    "inverter_status_vendor": ["gauge", "Inverter vendor-specific fault code"],
    "ac_current_amps": ["gauge", "Output bus current"],
    "ac_voltage_volts": ["gauge", "Output bus voltage"],
    'ac_frequency_hertz': ["gauge", "Output bus frequency"],
    'ac_power_watts': ["gauge", "Active/Reactive/Apparent AC power"],
    'ac_power_factor': ["gauge", "AC Phase factor"],
    'meter_event': ["gauge", 'Power meter event']
}

def export(d, m):
    yield ('dc_voltage_volts', {}, d.dc_voltage)
    yield ('dc_current_amps', {}, d.dc_current)
    yield ('dc_power_watts', {}, d.dc_power)
    yield ('ac_current_amps', {'phase': 'a', 'meter': 'production'}, d.ac_current_a)
    yield ('ac_current_amps', {'phase': 'b', 'meter': 'production'}, d.ac_current_b)
    yield ('ac_current_amps', {'phase': 'c', 'meter': 'production'}, d.ac_current_c)
    yield ('ac_power_watts', {'power': 'active', 'meter': 'production'}, d.ac_power)
    yield ('ac_power_watts', {'power': 'reactive', 'meter': 'production'}, d.ac_reactive_power)
    yield ('ac_power_watts', {'power': 'apparent', 'meter': 'production'}, d.ac_apparent_power)
    yield ('energy_watthours_total', {'meter': 'production'}, d.ac_energy)
    yield ('ac_voltage_volts', {'phase': 'ab', 'meter': 'production'}, d.ac_voltage_ab)
    yield ('ac_voltage_volts', {'phase': 'bc', 'meter': 'production'}, d.ac_voltage_bc)
    yield ('ac_voltage_volts', {'phase': 'ca', 'meter': 'production'}, d.ac_voltage_ca)
    yield ('ac_frequency_hertz', {'meter': 'production'}, d.frequency)
    yield ('inverter_status', {}, d.status_code)
    yield ('inverter_status_vendor', {}, d.status_vendor)

    for phase in ('a','b','c','total'):
        yield ('ac_current_amps', {'phase': phase, 'meter': 'mains'}, m.ac_current[phase])
        yield ('ac_power_watts',  {'phase': phase, 'meter': 'mains', 'power': 'active'}, m.ac_real_power[phase])
        yield ('ac_power_watts',  {'phase': phase, 'meter': 'mains', 'power': 'reactive'}, m.ac_reactive_power[phase])
        yield ('ac_power_watts',  {'phase': phase, 'meter': 'mains', 'power': 'apparent'}, m.ac_apparent_power[phase])
    for phase in ('a', 'b', 'c', 'average'):
        yield ('ac_power_factor', {'phase': phase, 'meter': 'mains'}, m.power_factor[phase])
        yield ('ac_voltage_volts', {'phase': phase, 'meter': 'mains'}, m.ac_voltage[phase])
    
    for meter in ('import', 'export'):
        yield ('energy_watthours_total', {'meter': meter}, m.energy_real[meter])

    yield ('ac_frequency_hertz', {'meter': 'mains'}, m.frequency)
    for event in m.events:
        yield ('meter_event', {'event': event}, float(m.events[event]))



def main():
    
    parser = argparse.ArgumentParser(prog='solaredge_modbus.py',
        description='Prometheus exporter for SolarEdge inverters',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-d', '--debug', action='store_true', help="Enable debug info in HTTP answers")
    parser.add_argument('-l', '--listen', default='localhost', help="Address to bind for the HTTP server")
    parser.add_argument('-p', '--listen-port', default='9150', help="Port of the HTTP server")
    parser.add_argument('host', help="Network address of the inverter to monitor")
    parser.add_argument('port', nargs='?', default=1502, help="Inverter Modbus/TCP port")

    args = parser.parse_args()

    inverter = sunspec.Inverter(host=args.host, port=args.port)

    def collect():
        date = datetime.datetime.now()

        common = inverter.common_block()
        inv = inverter.data()
        meters = inverter.meter(0)

        yield (date, 'firmware_version', {'version': common.version}, 1)
        for (m,a,v) in export(inv, meters):
            if v is not None:
                yield (date,m,a,v)
            
        
    prom.run(collect, metric_data, 'SolarEdge Modbus/TCP Exporter\n', 
        host=args.listen, port=args.listen_port, debug=args.debug)


if __name__ == '__main__':
    main()