Found what I think will be a much better way to read the Studer data, and its done locally through the LAN rather than going through the Studer cloud. I had made arrangements with the owner of the repositoryhttps://github.com/zocker-160/xcom-protocol and he updated his code to add support for communicating with Moxa when in TCP client mode so that the software could be run while still maintaining the system connection to the Studer portal.
I tested the Zocker-160 Xcom-LAN and found that I was able to read and write as expected. The testing is running Xcom -LAN on my laptop by calling it through a loop that writes the output to a csv file that I import into Calc. I seem to be able to successfully read a group of 27 values every on an average of about 2.0 seconds, but at this rate the portal connection gets overwhelmed and stops receiving values, probably due to request collisions. If I add a 5 second delay to the loop, the Studer connection seems to be stable. In a test that ran for just over 5 hours and read 3,000 sets, the time between readings was usually about 7 seconds. It’s faster than the RESTful integration I was using and seems much more reliable. Currently I am using the program on my PC and changing values at the command prompt, and also running value read tests. So far I have only run the read test for about 5 hours, I will try for a longer period. The next step will be to setup an MQTT broker on my PC and clients on both the PC and HA so HA can access the Xcom-protocol data.
If you want to try the python Xcom-protocol program, you can install it using the instructions on the github page: GitHub - zocker-160/xcom-protocol: Python library implementing Studer-Innotec Xcom protocol used by Xcom-232i and Xcom-LAN Below is the python script that I have been using call the program and read data from the Studer. The script includes almost everything that the portal’s Synoptic page includes. The script is run from a command prompt and take two arguments, writes a time stamp and a list of values to a csv file for importing into a spreadsheet. The first argument is the count of how many times to read the data list, and the second argument is how long of a delay in seconds to wait before starting the next read. I recommend setting the delay to 5 or higher if your system is connected to the Studer portal to avoid too many collisions.
Example of using the script:
python3 measurements10.py 10 5
the loop will read 10 values with a delay of 5 seconds after each read.
import sys
import datetime
import uuid
import time
# Your XcomLANTCP and other imports go here
from xcom_proto import XcomP as param
from xcom_proto import XcomC
from xcom_proto import XcomLANTCP
def main():
# Get the number of measurements and delay from command line arguments
num_measurements = int(sys.argv[1]) if len(sys.argv) > 1 else 1
delay = float(sys.argv[2]) if len(sys.argv) > 2 else 1
# Generate a unique identifier for file name
unique_id = str(uuid.uuid4())
with open(f'measurement_results_{unique_id}.csv', 'w') as file:
file.write(f'Time Stamp, ')
# ENERGY
file.write(f'#Has Solar (true/false), ') # 1 place holder
file.write(f'VS-1 PV Solar Energy (kWh), ') # 2A
file.write(f'VS-2 PV Solar Energy (kWh), ') # 2B
file.write(f'VS-1 Yesterday Solar Energy (kWh), ') # 3A
file.write(f'VS-2 Yesterday Solar Energy (kWh), ') # 3B
file.write(f'#Has Inverter (true/false), ') # 4 place holder
file.write(f'AC Energy in Current Day (kWh), ') # 5
file.write(f'AC Energy in Previous Day (kWh), ') # 6
file.write(f'AC_Energy Out Currrent Day (kWh), ') # 7
file.write(f'AC Energy out Previous Day (kWh), ') # 8
file.write(f'#Has Battery Status Processor (true/false), ') # 9 place holder
file.write(f'Battery Charge (Ah), ') # 10
file.write(f'Yesterday Battery Charge (Ah), ') # 11
file.write(f'Battery Discharge (Ah), ') # 12
file.write(f'Yesterday Battery Discharge (Ah), ') # 13
file.write(f'#Has AC Coupling (true/false), ') # 14 place holder
file.write(f'#AC Coupling (kWh), ') # 15 place holder
file.write(f'#Yesterday AC Coupling (kWh), ') # 16 place holder
# BATTERY
file.write(f'Battery Voltage (V), ') # 17
file.write(f'Battery Current (A), ') # 18
file.write(f'Battery SOC (%), ') # 19
file.write(f'Battery Temperature (°C), ') # 20
file.write(f'Battery Cycle Phase, ') # 21
# POWER
file.write(f'VS-1 PV Solar Power (kW), ') # 22A
file.write(f'VS-2 PV Solar Power (kW), ') # 22B
file.write(f'AC Power In (kW), ') # 23
file.write(f'AC Power Out (kW), ') # 24
file.write(f'Battery Power (W), ') # 25
file.write(f'#Has AC Coupling (true/false), ') # 26 place holder
file.write(f'#AC Coupling (kW), ') # 27 place holder
file.write(f'AC Current In (A), ') # 28
file.write(f'AC Current Out (A), ') # 29
file.write(f'AC Voltage In (V), ') # 30
file.write(f'AC Voltage Out (V), ') # 31
file.write(f'AC Frequncy In (Hz), ') # 32
file.write(f'AC Frequncy Out (Hz)\n') # 33 line feed at end
with XcomLANTCP(port=4001) as xcom:
measurements = 0
while measurements < num_measurements:
has_solar = 'NaN' # 1 place holder
solar_prod_vs1 = xcom.getValueByID(15017, XcomC.TYPE_FLOAT, dstAddr=701) # 2A
solar_prod_vs2 = xcom.getValueByID(15017, XcomC.TYPE_FLOAT, dstAddr=702) # 2B
yesterday_solar_energy_vs1 = xcom.getValueByID(15026, XcomC.TYPE_FLOAT, dstAddr=701) # 3A
yesterday_solar_energy_vs2 = xcom.getValueByID(15026, XcomC.TYPE_FLOAT, dstAddr=702) # 3B
hasInverter = 'NaN' # 4 place holder
grid_prod = xcom.getValue(param.AC_ENERGY_IN_CURR_DAY) # 5
ac_energy_in_prev_day = xcom.getValue(param.AC_ENERGY_IN_PREV_DAY) # 6
ac_energy_out_curr_day = xcom.getValue(param.AC_ENERGY_OUT_CURR_DAY) # 7
ac_energy_out_prev_day = xcom.getValue(param.AC_ENERGY_OUT_PREV_DAY) # 8
has_battery_status_processor = 'NaN' # 9 place holder
battery_charge = xcom.getValueByID(7007, XcomC.TYPE_FLOAT) # 10
yesterday_battery_charge = xcom.getValueByID(7009, XcomC.TYPE_FLOAT) # 11
battery_discharge = xcom.getValueByID(7008, XcomC.TYPE_FLOAT) # 12
yesterday_battery_discharge = xcom.getValueByID(7010, XcomC.TYPE_FLOAT) # 13
has_ac_coupling = 'NaN' # 14 place holder
ac_coupling = 'NaN' # 15 place holder
yesterday_ac_coupling = 'NaN' # 16 place holder
battery_voltage = xcom.getValue(param.BATT_VOLTAGE) # 17
battery_current = xcom.getValue(param.BATT_CURRENT) # 18
soc = xcom.getValue(param.BATT_SOC) # 19
battery_temp = xcom.getValue(param.BATT_TEMP) # 20
batt_cycle_phase = xcom.getValueByID(3010, XcomC.TYPE_SHORT_ENUM) # 21 parameter.py is for Vario Track instead of Xtender
solar_power_vs1 = xcom.getValueByID(15010, XcomC.TYPE_FLOAT, dstAddr=701) # 22A
solar_power_vs2 = xcom.getValueByID(15010, XcomC.TYPE_FLOAT, dstAddr=702) # 22B
gridpower = xcom.getValue(param.AC_POWER_IN) # 23
houseload = xcom.getValue(param.AC_POWER_OUT) # 24
battpower = xcom.getValueByID(7003, XcomC.TYPE_FLOAT) # 25
has_ac_coupling = 'NaN' # 26 place holder
ac_coupling = 'NaN' # 27 place holder
ac_in_current = xcom.getValueByID(3012, XcomC.TYPE_FLOAT) # 28
ac_out_current = xcom.getValueByID(3022, XcomC.TYPE_FLOAT) # 29
ac_voltage_in = xcom.getValue(param.AC_VOLTAGE_IN) # 30
ac_voltage_out = xcom.getValue(param.AC_VOLTAGE_OUT) # 31
ac_freq_in = xcom.getValue(param.AC_FREQ_IN) # 32
ac_freq_out = xcom.getValue(param.AC_FREQ_OUT) # 33
file.write(f'{datetime.datetime.now()}, ')
file.write(f'{has_solar}, ') # 1 place holder
file.write(f'{solar_prod_vs1}, ') # 2A
file.write(f'{solar_prod_vs2}, ') # 2B
file.write(f'{yesterday_solar_energy_vs1}, ') # 3A
file.write(f'{yesterday_solar_energy_vs2}, ') # 3B
file.write(f'{hasInverter}, ') # 4 place holder
file.write(f'{grid_prod}, ') # 5
file.write(f'{ac_energy_in_prev_day}, ') # 6
file.write(f'{ac_energy_out_curr_day}, ') # 7
file.write(f'{ac_energy_out_prev_day}, ') # 8
file.write(f'{has_battery_status_processor}, ') # 9 place holder
file.write(f'{battery_charge}, ') # 10
file.write(f'{yesterday_battery_charge}, ') # 11
file.write(f'{battery_discharge}, ') # 12
file.write(f'{yesterday_battery_discharge}, ') # 13
file.write(f'{has_ac_coupling}, ') # 14 place holder
file.write(f'{ac_coupling}, ') # 15 place holder
file.write(f'{yesterday_ac_coupling}, ') # 16 place holder
file.write(f'{battery_voltage}, ') # 17
file.write(f'{battery_current}, ') # 18
file.write(f'{soc}, ') # 19
file.write(f'{battery_temp}, ') # 20
file.write(f'{batt_cycle_phase}, ') # 21
file.write(f'{solar_power_vs1}, ') # 22A
file.write(f'{solar_power_vs2}, ') # 22B
file.write(f'{gridpower}, ') # 23
file.write(f'{houseload}, ') # 24
file.write(f'{battpower/1000}, ') # 25
file.write(f'{has_ac_coupling}, ') # 26 place holder
file.write(f'{ac_coupling}, ') # 27 place holder
file.write(f'{ac_in_current}, ') # 28
file.write(f'{ac_out_current}, ') # 29
file.write(f'{ac_voltage_in}, ') # 30
file.write(f'{ac_voltage_out}, ') # 31
file.write(f'{ac_freq_in}, ') # 32
file.write(f'{ac_freq_out}\n') # 33 line feed at end
# print(measurements)
time.sleep(delay)
measurements += 1
if __name__ == "__main__":
main()