# Copyright 2021 Marcel Konstapel # https://www.meezenest.nl/mees # # This file is part of SolarGeneratorDatalogger. # A set of programs and scripts to log all the available data from a self build solar generator. # It runs on a Raspberry Pi Zero 1 W with Raspbian # # It logs data from a EPEVER Soalrge Charge Controller (via Bluetooth) and an EPEVER solar charger (via RS485-USB). # The data is analyzed and stored in a file. The data is also available via a web browser over Wifi # (Raspberry Pi acts as access point). # Future plan is to implement LoRa so the data can be accessed remotely. # # Program is configured via the file "config.json" # # Example of config_file: # # { # "epever_data_file": "epever_data.json", # "RS485_port": "/dev/ttyXRUSB0", # "RS485_address": 1 # } # # Usage of program: # # -c, --config_file : reads the most important registers of the charge # controller and writes it to the file specified in the # config_file # -s, --set_date : sets the date and time from the given argument with # format: YYYY-MM-DD HH:MM:SS # -l, --load : switches load on or off as given by the argument") # -d, --dump_file : dumps all the registers in the specified file # # # A lot of effort is made to catch all possible communication errors, so the program never hangs. # When an error occurs, the program exits and has probably not written any data to 'epever_data_file' # But at least we did not crash! Check the date of the data file to make sure the data is written or not. # # Example of epever_data_file (JSON format): # #{ # "solar_voltage": 0, # "solar_current": 0, # "solar_power": 0, # "load_voltage": 13.11, # "load_current": 0, # "load_power": 0, # "battery_voltage": 13.14, # "battery_current": 0, # "battery_power": 0, # "controller_temp": 18.1, # "ambient_temp": 16.42, # "generated_energy_today": 0, # "controller_date_time": "2021-11-12 15:56:52", # "battery_status": { # "wrong_identifaction_for_rated_voltage": false, # "battery_inner_resistence_abnormal": false, # "temperature_warning_status": "NORMAL", # "battery_status": "NORMAL" # }, # "controller_status": { # "input_voltage_status": "NORMAL", # "charging_mosfet_is_short_circuit": false, # "charging_or_anti_reverse_mosfet_is_open_circuit": false, # "anti_reverse_mosfet_is_short_circuit": false, # "input_over_current": false, # "load_over_current": false, # "load_short_circuit": false, # "load_mosfet_short_circuit": false, # "disequilibrium_in_three_circuits": false, # "pv_input_short_circuit": false, # "charging_status": "NO_CHARGING", # "fault": false, # "running": true # }, # "load_on": true #} # # This code is based on: # https://github.com/rosswarren/epevermodbus # https://minimalmodbus.readthedocs.io/en/master/index.html # https://github.com/kasbert/epsolar-tracer (Linux driver for CC-USB-RS485-150U USB to RS485 cable) # # Thanks for sharing your knowledge! # # V0.1 2021-11-12 # - First working program # - Generic read works # - Dump all registers works # - Set date does not work yet # - Make LOAD-switch availible through command line arguments # # V0.2 2021-11-15 # - Set date and time works # - Load switch can now be set from command line # - Started to implement programming of the controller, but stumbled across a problem: when the # controller is in lithium mode, it seems like it is not possible to program some registers. # This functionality is not documented by EPEVER. Bummer! # # This Python3 program gathers the data from the Liontron BMS over Bluetooth BLE. # # SolarGeneratorDatalogger is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # SolarGeneratorDatalogger is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with SolarGeneratorDatalogger. If not, see . # import json import sys import time import getopt from json.decoder import JSONDecodeError from datetime import datetime from itertools import count from multiprocessing import Process from epever_control import EpeverChargeController # This function is very much a work in progress. # It seems that is is not possible to program the registers when the controller is in lithium mode. This part of the protocol (lithium programming) # is undocumented by EPEVER and no one in the community has figured it out yet. I could try to reverse engineer this, but for now I decided not to. # The program as it is has all the functionality I need for my application, so that's all folk! def dump_all_registers(): rawdat={} #Address range 0x3000 rawdat['rated_solar_voltage'] = controller.get_rated_solar_voltage() rawdat['rated_solar_current'] = controller.get_rated_solar_current() rawdat['rated_solar_power'] = controller.get_rated_solar_power_l() + 256*controller.get_rated_solar_power_h() rawdat['rated_charging_voltage'] = controller.get_rated_charging_voltage() rawdat['rated_charging_current'] = controller.get_rated_charging_current() rawdat['rated_charging_power'] = controller.get_rated_charging_power_l() + 256 * controller.get_rated_charging_power_h() rawdat['charging_mode'] = controller.get_charging_mode() rawdat['rated_load_current'] = controller.get_rated_load_current() # Address range 0x3100 rawdat['solar_voltage'] = controller.get_solar_voltage() rawdat['solar_current'] = controller.get_solar_current() rawdat['solar_power'] = controller.get_solar_power_l() + 256 * controller.get_solar_power_h() rawdat['battery_voltage'] = controller.get_battery_voltage() rawdat['battery_current'] = controller.get_battery_current() rawdat['battery_power'] = controller.get_battery_power_l() + 256 * controller.get_battery_power_h() rawdat['load_voltage'] = controller.get_load_voltage() rawdat['load_current'] = controller.get_load_current() rawdat['load_power'] = controller.get_load_power_l() + 256 * controller.get_load_power_h() rawdat['battery_temperature'] = controller.get_battery_temperature() rawdat['controller_temperature'] = controller.get_controller_temperature() rawdat['heatsink_temperature'] = controller.get_heatsink_temperature() rawdat['battery_state_of_charge'] = controller.get_battery_state_of_charge() rawdat['remote_battery_temperature'] = controller.get_remote_battery_temperature() rawdat['battery_real_rated_voltage'] = controller.get_battery_real_rated_voltage() # Address range 0x3200 rawdat['battery_status'] = controller.get_battery_status() rawdat['charging_equipment_status'] = controller.get_charging_equipment_status() # Address 0x3300 rawdat['maximum_solar_voltage_today'] = controller.get_maximum_solar_voltage_today() rawdat['minimum_solar_voltage_today'] = controller.get_minimum_solar_voltage_today() rawdat['maximum_battery_voltage_today'] = controller.get_maximum_battery_voltage_today() rawdat['minimum_battery_voltage_today'] = controller.get_minimum_battery_voltage_today() rawdat['consumed_energy_today'] = controller.get_consumed_energy_today_l() + 256 * controller.get_consumed_energy_today_h() rawdat['consumed_energy_month'] = controller.get_consumed_energy_month_l() + 256 * controller.get_consumed_energy_month_h() rawdat['consumed_energy_year'] = controller.get_consumed_energy_year_l() + 256 * controller.get_consumed_energy_year_h() rawdat['consumed_energy_total'] = controller.get_consumed_energy_total_l() + 256 * controller.get_consumed_energy_total_h() rawdat['generated_energy_today'] = controller.get_generated_energy_today_l() + 256 * controller.get_generated_energy_today_h() rawdat['generated_energy_month'] = controller.get_generated_energy_month_l() + 256 * controller.get_generated_energy_month_h() rawdat['generated_energy_year'] = controller.get_generated_energy_year_l() + 256 * controller.get_generated_energy_year_h() rawdat['generated_energy_total'] = controller.get_generated_energy_total_l() + 256 * controller.get_generated_energy_total_h() rawdat['carbon_reduction'] = controller.get_carbon_reduction_l() + 256 * controller.get_carbon_reduction_h() rawdat['battery_net_current'] = controller.get_battery_net_current_l() + 256 * controller.get_battery_net_current_h() rawdat['battery_temperature'] = controller.get_battery_temperature() rawdat['ambient_temperature'] = controller.get_ambient_temperature() # Address range 0x9000 rawdat['battery_type'] = controller.get_battery_type() rawdat['battery_capacity'] = controller.get_battery_capacity() rawdat['temperature_compensation_coefficient'] = controller.get_temperature_compensation_coefficient() rawdat['over_voltage_disconnect_voltage'] = controller.get_over_voltage_disconnect_voltage() rawdat['charging_limit_voltage'] = controller.get_charging_limit_voltage() rawdat['over_voltage_reconnect_voltage'] = controller.get_over_voltage_reconnect_voltage() rawdat['equalize_charging_voltage'] = controller.get_equalize_charging_voltage() rawdat['boost_charging_voltage'] = controller.get_boost_charging_voltage() rawdat['float_charging_voltage'] = controller.get_float_charging_voltage() rawdat['boost_reconnect_charging_voltage'] = controller.get_boost_reconnect_charging_voltage() rawdat['low_voltage_reconnect_voltage'] = controller.get_low_voltage_reconnect_voltage() rawdat['under_voltage_recover_voltage'] = controller.get_under_voltage_recover_voltage() rawdat['under_voltage_warning_voltage'] = controller.get_under_voltage_warning_voltage() rawdat['low_voltage_disconnect_voltage'] = controller.get_low_voltage_disconnect_voltage() rawdat['discharging_limit_voltage'] = controller.get_discharging_limit_voltage() rawdat['controller_date_time'] = controller.get_datetime() rawdat['equalization_charging_cycle'] = controller.get_equalization_charging_cycle() rawdat['battery_temp_upper_warning_limit'] = controller.get_battery_temp_upper_warning_limit() rawdat['battery_temp_lower_warning_limit'] = controller.get_battery_temp_lower_warning_limit() rawdat['controller_inner_temp_upper_limit'] = controller.get_controller_inner_temp_upper_limit() rawdat['controller_inner_temp_upper_limit_recover'] = controller.get_controller_inner_temp_upper_limit_recover() rawdat['power_comp_temp_upper_limit'] = controller.get_power_comp_temp_upper_limit() rawdat['power_comp_temp_upper_limit_recover'] = controller.get_power_comp_temp_upper_limit_recover() rawdat['line_impedance'] = controller.get_line_impedance() rawdat['night_time_threshold_volt'] = controller.get_night_time_threshold_volt() rawdat['light_signal_startup_delay'] = controller.get_light_signal_startup_delay() rawdat['day_time_threshold_volt'] = controller.get_day_time_threshold_volt() rawdat['light_signal_turnoff_delay'] = controller.get_light_signal_turnoff_delay() rawdat['controling_mode'] = controller.get_controling_mode() rawdat['working_time_lenght_1'] = controller.get_working_time_lenght_1() rawdat['working_time_lenght_2'] = controller.get_working_time_lenght_2() rawdat['turn_on_time_1_sec'] = controller.get_turn_on_time_1_sec() rawdat['turn_on_time_1_min'] = controller.get_turn_on_time_1_min() rawdat['turn_on_time_1_hours'] = controller.get_turn_on_time_1_hours() rawdat['turn_off_time_1_sec'] = controller.get_turn_off_time_1_sec() rawdat['turn_off_time_1_min'] = controller.get_turn_off_time_1_min() rawdat['turn_off_time_1_hours'] = controller.get_turn_off_time_1_hours() rawdat['turn_on_time_2_sec'] = controller.get_turn_on_time_2_sec() rawdat['turn_on_time_2_min'] = controller.get_turn_on_time_2_min() rawdat['turn_on_time_2_hours'] = controller.get_turn_on_time_2_hours() rawdat['turn_off_time_2_sec'] = controller.get_turn_off_time_2_sec() rawdat['turn_off_time_2_min'] = controller.get_turn_off_time_2_min() rawdat['turn_off_time_2_hours'] = controller.get_turn_off_time_2_hours() rawdat['length_of_night'] = controller.get_length_of_night() rawdat['battery_rated_voltage'] = controller.get_battery_rated_voltage() rawdat['load_timing_control_selection'] = controller.get_load_timing_control_selection() rawdat['default_load_on_off_in_manual_mode'] = controller.get_default_load_on_off_in_manual_mode() rawdat['equalize_duration'] = controller.get_equalize_duration() rawdat['boost_duration'] = controller.get_boost_duration() rawdat['battery_discharge'] = controller.get_battery_discharge() rawdat['battery_charge'] = controller.get_battery_charge() rawdat['management_mode'] = controller.get_management_mode() # Address range 0x0 rawdat['manual_control'] = controller.get_manual_control() rawdat['enable_load_test_mode'] = controller.get_enable_load_test_mode() rawdat['force_load_on'] = controller.get_force_load_on() # Address range 0x2000 rawdat['is_device_over_temperature'] = controller.is_device_over_temperature() rawdat['is_night'] = controller.is_night() print (json.dumps(rawdat, indent=1, sort_keys=False)) with open(dump_file, 'w') as f: json.dump(rawdat, f, indent=1) # Reads the given config-file and programs all the registers defined in this file to the solar controller def program_registers(): print("Program the registers specified in the register file:", register_file) # read configuration file try: with open(register_file, 'r') as myfile: register_data=myfile.read() except FileNotFoundError: print("Configuration file <"+ register_file +"> not found.") sys.exit(); # parse configuration file try: register_obj = json.loads(register_data) except JSONDecodeError as e: print("Error decoding configuration file. Not JSON format?") sys.exit() except TypeError as e: print("Error decoding configuration file: JSON objects not str, bytes or bytearray.") sys.exit() except KeyError: print("Error reading configuration file: needed fields not found.") sys.exit() if 'battery_type' in register_obj: #controller.set_battery_type(register_obj['battery_type']): print("battery_type =", register_obj['battery_type']) if 'battery_capacity' in register_obj: #controller.set_battery_capacity(register_obj['battery_capacity']) print("battery_capacity =", register_obj['battery_capacity']) if 'temperature_compensation_coefficient' in register_obj: #controller.set_temperature_compensation_coefficient(register_obj['temperature_compensation_coefficient']) print("temperature_compensation_coefficient =", register_obj['temperature_compensation_coefficient']) if 'over_voltage_disconnect_voltage' in register_obj: #controller.set_over_voltage_disconnect_voltage(register_obj['over_voltage_disconnect_voltage']) print("over_voltage_disconnect_voltage =", register_obj['over_voltage_disconnect_voltage']) if 'charging_limit_voltage' in register_obj: #controller.set_charging_limit_voltage(register_obj['charging_limit_voltage']) print("charging_limit_voltage =", register_obj['charging_limit_voltage']) if 'over_voltage_reconnect_voltage' in register_obj: #controller.set_over_voltage_reconnect_voltage(register_obj['charging_limit_voltage']) print("over_voltage_reconnect_voltage =", register_obj['over_voltage_reconnect_voltage']) if 'equalize_charging_voltage' in register_obj: #controller.set_equalize_charging_voltage(register_obj['charging_limit_voltage']) print("equalize_charging_voltage =", register_obj['equalize_charging_voltage']) if 'boost_charging_voltage' in register_obj: #controller.set_boost_charging_voltage(register_obj['charging_limit_voltage']) print("boost_charging_voltage =", register_obj['boost_charging_voltage']) if 'float_charging_voltage' in register_obj: #controller.set_float_charging_voltage(register_obj['charging_limit_voltage']) print("float_charging_voltage =", register_obj['float_charging_voltage']) if 'boost_reconnect_charging_voltage' in register_obj: #controller.set_boost_reconnect_charging_voltage(register_obj['charging_limit_voltage']) print("boost_reconnect_charging_voltage =", register_obj['boost_reconnect_charging_voltage']) if 'low_voltage_reconnect_voltage' in register_obj: #controller.set_low_voltage_reconnect_voltage(register_obj['charging_limit_voltage']) print("low_voltage_reconnect_voltage =", register_obj['low_voltage_reconnect_voltage']) if 'under_voltage_recover_voltage' in register_obj: #controller.set_under_voltage_recover_voltage(register_obj['charging_limit_voltage']) print("under_voltage_recover_voltage =", register_obj['under_voltage_recover_voltage']) if 'under_voltage_warning_voltage' in register_obj: #controller.set_under_voltage_warning_voltage(register_obj['charging_limit_voltage']) print("under_voltage_warning_voltage =", register_obj['under_voltage_warning_voltage']) if 'low_voltage_disconnect_voltage' in register_obj: #controller.set_low_voltage_disconnect_voltage(register_obj['charging_limit_voltage']) print("low_voltage_disconnect_voltage =", register_obj['low_voltage_disconnect_voltage']) if 'discharging_limit_voltage' in register_obj: #controller.set_discharging_limit_voltage(register_obj['charging_limit_voltage']) print("discharging_limit_voltage =", register_obj['discharging_limit_voltage']) if 'datetime_sec_min' in register_obj: #controller.set_datetime_sec_min(self, value): print("datetime_sec_min =", register_obj['datetime_sec_min']) if 'datetime_hour_day' in register_obj: #controller.set_datetime_hour_day(self, value): print("datetime_hour_day =", register_obj['datetime_hour_day']) if 'datetime_month_year' in register_obj: #controller.set_datetime_month_year(self, value): print("datetime_month_year =", register_obj['datetime_month_year']) if 'equalization_charging_cycle' in register_obj: #controller.set_equalization_charging_cycle(self, value): print("equalization_charging_cycle =", register_obj['equalization_charging_cycle']) if 'battery_temp_upper_warning_limit' in register_obj: #controller.set_battery_temp_upper_warning_limit(self, value): print("battery_temp_upper_warning_limit =", register_obj['battery_temp_upper_warning_limit']) if 'battery_temp_lower_warning_limit' in register_obj: #controller.set_battery_temp_lower_warning_limit(self, value): print("battery_temp_lower_warning_limit =", register_obj['battery_temp_lower_warning_limit']) if 'controller_inner_temp_upper_limit' in register_obj: #controller.set_controller_inner_temp_upper_limit(self, value): print("controller_inner_temp_upper_limit =", register_obj['controller_inner_temp_upper_limit']) if 'controller_inner_temp_upper_limit_recover' in register_obj: #controller.set_controller_inner_temp_upper_limit_recover(self, value): print("controller_inner_temp_upper_limit_recover =", register_obj['controller_inner_temp_upper_limit_recover']) if 'power_comp_temp_upper_limit' in register_obj: #controller.set_power_comp_temp_upper_limit(self, value): print("equalization_charging_cycle =", register_obj['equalization_charging_cycle']) if 'power_comp_temp_upper_limit_recover' in register_obj: #controller.set_power_comp_temp_upper_limit_recover(self, value): print("power_comp_temp_upper_limit_recover =", register_obj['power_comp_temp_upper_limit_recover']) if 'line_impedance' in register_obj: #controller.set_line_impedance(self, value): print("line_impedance =", register_obj['line_impedance']) if 'night_time_threshold_volt' in register_obj: #controller.set_night_time_threshold_volt(self, value): print("night_time_threshold_volt =", register_obj['night_time_threshold_volt']) if 'light_signal_startup_delay' in register_obj: #controller.set_light_signal_startup_delay(self, value): print("light_signal_startup_delay =", register_obj['light_signal_startup_delay']) if 'day_time_threshold_volt' in register_obj: #controller.set_day_time_threshold_volt(self, value): print("day_time_threshold_volt =", register_obj['day_time_threshold_volt']) if 'light_signal_turnoff_delay' in register_obj: #controller.set_light_signal_turnoff_delay(self, value) print("light_signal_turnoff_delay =", register_obj['light_signal_turnoff_delay']) if 'controling_mode' in register_obj: #controller.set_controling_mode(self, value): print("controling_mode =", register_obj['controling_mode']) if 'working_time_lenght_1' in register_obj: #controller.set_working_time_lenght_1(self, value): print("working_time_lenght_1 =", register_obj['working_time_lenght_1']) if 'working_time_lenght_2' in register_obj: #controller.set_working_time_lenght_2(self, value): print("working_time_lenght_2 =", register_obj['working_time_lenght_2']) if 'turn_on_time_1_sec' in register_obj: #controller.set_turn_on_time_1_sec(self, value): print("turn_on_time_1_sec =", register_obj['turn_on_time_1_sec']) if 'turn_on_time_1_min' in register_obj: #controller.set_turn_on_time_1_min(self, value): print("turn_on_time_1_min =", register_obj['turn_on_time_1_min']) if 'turn_on_time_1_hours' in register_obj: #controller.set_turn_on_time_1_hours(self, value): print("turn_on_time_1_hours =", register_obj['turn_on_time_1_hours']) if 'turn_off_time_1_sec' in register_obj: #controller.set_turn_off_time_1_sec(self, value): print("turn_off_time_1_sec =", register_obj['turn_off_time_1_sec']) if 'turn_off_time_1_min' in register_obj: #controller.set_turn_off_time_1_min(self, value): print("turn_off_time_1_min =", register_obj['turn_off_time_1_min']) if 'turn_off_time_1_hours' in register_obj: #controller.set_turn_off_time_1_hours(self, value): print("turn_off_time_1_hours =", register_obj['turn_off_time_1_hours']) if 'turn_on_time_2_sec' in register_obj: #controller.set_turn_on_time_2_sec(self, value): print("turn_on_time_2_sec =", register_obj['turn_on_time_2_sec']) if 'turn_on_time_2_min' in register_obj: #controller.set_turn_on_time_2_min(self, value): print("turn_on_time_2_min =", register_obj['turn_on_time_2_min']) if 'turn_on_time_2_hours' in register_obj: #controller.set_turn_on_time_2_hours(self, value): print("turn_on_time_2_hours =", register_obj['turn_on_time_2_hours']) if 'turn_off_time_2_sec' in register_obj: #controller.set_turn_off_time_2_sec(self, value): print("turn_off_time_2_sec =", register_obj['turn_off_time_2_sec']) if 'turn_off_time_2_min' in register_obj: #controller.set_turn_off_time_2_min(self, value): print("turn_off_time_2_min =", register_obj['turn_off_time_2_min']) if 'turn_off_time_2_hours' in register_obj: #controller.set_turn_off_time_2_hours(self, value): print("turn_off_time_2_hours =", register_obj['turn_off_time_2_hours']) if 'length_of_night' in register_obj: #controller.set_length_of_night(self, value): print("length_of_night =", register_obj['length_of_night']) if 'battery_rated_voltage' in register_obj: #controller.set_battery_rated_voltage(self, value): print("battery_rated_voltage =", register_obj['battery_rated_voltage']) if 'load_timing_control_selection' in register_obj: #controller.set_load_timing_control_selection(self, value): print("load_timing_control_selection =", register_obj['load_timing_control_selection']) if 'default_load_on_off_in_manual_mode' in register_obj: #controller.set_default_load_on_off_in_manual_mode(self, value): print("default_load_on_off_in_manual_mode =", register_obj['default_load_on_off_in_manual_mode']) if 'equalize_duration' in register_obj: #controller.set_equalize_duration(self, value): print("equalize_duration =", register_obj['equalize_duration']) if 'boost_duration' in register_obj: #controller.set_boost_duration(self, value): print("boost_duration =", register_obj['boost_duration']) if 'battery_discharge' in register_obj: #controller.set_battery_discharge(self, value): print("battery_discharge =", register_obj['battery_discharge']) if 'battery_charge' in register_obj: #controller.set_battery_charge(self, value): print("battery_charge =", register_obj['battery_charge']) if 'management_mode' in register_obj: #controller.set_management_mode(self, value): print("management_mode =", register_obj['management_mode']) print("This functionality is not implemented yet. Nothing is actually written to the solar charger.") config_file ="" set_date ="" load_switch="" register_file="" dump_file="" argv = sys.argv[1:] try: options, args = getopt.getopt(argv, "c:s:l:p:d:", ["config_file =", "set_date =", "load_switch =", "register_file=", "dump_file ="]) except: print("epever-charge-controller can read and write the registers of a EPEVER solar") print("charge controller using the Modbus protocol.") print("") print("-c, --config_file : reads the most important registers of the charge") print(" controller and writes it to the file specified in the") print(" config_file") print("-s, --set_date : sets the date and time from the given argument with") print(" format: YYYY-MM-DD HH:MM:SS") print("-l, --load : switches load on or off as given by the argument") print("-p, --program : programs all the registers of the solar charger given in") print(" the specified register file.") print("-d, --dump_file : dumps all the registers in the specified file") sys.exit() for name, value in options: if name in ['-c', '--config_file']: config_file = value elif name in ['-s', '--set_date']: set_date = value elif name in ['-l', '--load']: load_switch = value elif name in ['-p', '--program']: register_file = value elif name in ['-d', '--dump']: dump_file = value if not config_file: print("No configuration file given.") sys.exit(); # read configuration file try: with open(config_file, 'r') as myfile: data=myfile.read() except FileNotFoundError: print("Configuration file <"+ config_file +"> not found.") sys.exit(); # parse configuration file try: obj = json.loads(data) tmp = obj['epever_data_file'] tmp1 = obj['RS485_port'] tmp2 = obj['RS485_address'] except JSONDecodeError as e: print("Error decoding configuration file. Not JSON format?") sys.exit() except TypeError as e: print("Error decoding configuration file: JSON objects not str, bytes or bytearray.") sys.exit() except KeyError: print("Error reading configuration file: needed fields not found.") sys.exit() # show values from config file print("Using RRS485 port : " + str(obj['RS485_port'])) print("Using RRS485 address : " + str(obj['RS485_address'])) print("Saving EPEVER data to: " + str(obj['epever_data_file'])) controller = EpeverChargeController(obj['RS485_port'], obj['RS485_address']) if set_date: try: iso_date = datetime.fromisoformat(set_date) except: print("Error in date and time. Date and time should be in ISO format.") sys.exit() print("Setting the time and date to", iso_date) datetime={} datetime['year'] = int(iso_date.strftime('%Y')) - 2000 datetime['month'] = int(iso_date.strftime('%m')) datetime['day'] = int(iso_date.strftime('%d')) datetime['hours'] = int(iso_date.strftime('%H')) datetime['minutes'] = int(iso_date.strftime('%M')) datetime['seconds'] = int(iso_date.strftime('%S')) controller.write_date(datetime) elif load_switch: if (load_switch.lower()=='on'): print("Switching load ON.") controller.switch_load_on() elif (load_switch.lower()=='off'): print("Switching load OFF.") controller.switch_load_off() else: print("No argument given. Cannot switch load.") elif register_file: program_registers(); elif dump_file: print("Dumping all registers to file: "+ dump_file) dump_all_registers() else: status = 1 print("Enable heater function") controller.enable_heater() while (1): time.sleep(3) # Sleep for 3 seconds print ("Retrieving all known registers.") rawdat={} rawdat['ID'] = controller.get_id() rawdat['Wind direction'] = controller.get_wind_direction() rawdat['Wind speed'] = controller.get_wind_speedl() rawdat['Wind gust'] = controller.get_wind_gust() rawdat['Rain last hour'] = controller.get_rain() rawdat['Rain last 24 hours'] = controller.get_rain_last24() rawdat['Temperature'] = controller.get_temperature() rawdat['Humidity'] = controller.get_humidity() rawdat['Pressure'] = controller.get_pressure() rawdat['Temp backup'] = controller.get_temperature_backup() rawdat['Status bits'] = controller.get_status_bits() print (json.dumps(rawdat, indent=1, sort_keys=False))