[new binding] Logger LSW for Sofar/Omnik/IE for SolarmanPV based on protocol v5 (iGEN tech)

Hello,

I have created a binding integrating with mentioned logger. It works with loggers with serial number 17xxxxx which are based on version 5 of the solarman protocol. It connects locally to the 8899 port of the logger.

It works fairly stable and I would test it for a week or two to catch corner cases before releasing it.

However I am wondering how the binding should behave when inverter goes offline because the low energy production, through the night? As for know I just setting the offline state of the thing bounded to the inverter/logger. Also, daily production is resetting after inverter start in the morning, which is not very good representation of reality.

Current supported channels:

			<channel id="operatingState" typeId="operatingState-channel"/>
			<channel id="fault" typeId="fault-channel"/>
			<channel id="gridAVoltage" typeId="gridVoltage-channel">
				<label>Grid A Voltage</label>
				<description>DC voltage of the first grid connected to the inverter</description>
			</channel>
			<channel id="gridACurrent" typeId="gridCurrent-channel">
				<label>Grid A Current</label>
				<description>DC current of the first grid connected to the inverter</description>
			</channel>
			<channel id="gridBVoltage" typeId="gridVoltage-channel">
				<label>Grid B Voltage</label>
				<description>DC voltage of the second grid connected to the inverter</description>
			</channel>
			<channel id="gridBCurrent" typeId="gridCurrent-channel">
				<label>Grid B Current</label>
				<description>DC current of the second grid connected to the inverter</description>
			</channel>
			<channel id="gridAPower" typeId="gridPower-channel">
				<label>Grid A Power</label>
				<description>Power of the first grid connected to the inverter</description>
			</channel>
			<channel id="gridBPower" typeId="gridPower-channel">
				<label>Grid B Power</label>
				<description>Power of the second grid connected to the inverter</description>
			</channel>
			<channel id="outputActivePower" typeId="activePower-channel"/>
			<channel id="outputReactivePower" typeId="reactivePower-channel"/>
			<channel id="outputFrequency" typeId="outputFrequency-channel"/>
			<channel id="1stPhaseVoltage" typeId="acVoltage-channel">
				<label>1st Phase Voltage</label>
				<description>Voltage of the 1st phase genarated by inverter</description>
			</channel>
			<channel id="1stPhaseCurrent" typeId="acCurrent-channel">
				<label>1st Phase Current</label>
				<description>Current of the 1st phase generated by inverter</description>
			</channel>
			<channel id="2ndPhaseVoltage" typeId="acVoltage-channel">
				<label>2nd Phase Voltage</label>
				<description>Voltage of the 2nd phase genarated by inverter</description>
			</channel>
			<channel id="2ndPhaseCurrent" typeId="acCurrent-channel">
				<label>2nd Phase Current</label>
				<description>Current of the 2nd phase generated by inverter</description>
			</channel>
			<channel id="3rdPhaseVoltage" typeId="acVoltage-channel">
				<label>3rd Phase Voltage</label>
				<description>Voltage of the 3rd phase genarated by inverter</description>
			</channel>
			<channel id="3rdPhaseCurrent" typeId="acCurrent-channel">
				<label>3rd Phase Current</label>
				<description>Current of the 3rd phase generated by inverter</description>
			</channel>
			<channel id="totalEnergyProduction" typeId="totalEnergyProduction-channel"/>
			<channel id="totalGenerationTime" typeId="totalGenerationTime-channel"/>
			<channel id="todayEnergyProduction" typeId="todayEnergyProduction-channel"/>
			<channel id="todayGenerationTime" typeId="todayGenerationTime-channel"/>
			<channel id="inverterModuleTemperature" typeId="temperature-channel">
				<label>Module Temperature</label>
				<description>Module temperature</description>
			</channel>
			<channel id="inverterInnerTemperature" typeId="temperature-channel">
				<label>Inner Temperature</label>
				<description>Inner temperature</description>
			</channel>

Project / repo / binaries will be here - GitHub - ptrbojko/openhab-lsw4inverter-binding

Regards

1 Like

Hi,

Can U please share the script You’ve used to connect to a logger ?

Michal

Here is the initial release

This is a binding so it must be deployed to your openhab instance. As for now - logs shouts more that it should for production systems, have it mind.

I wouldn’t consider this as a daily use binding, but if someone want to help me with tests - this for you.

I have patched unit handling and some minor bugs here and there. Also handling states of connection with logger should be better.

2 Likes

Looks great, is there any example how to setup the thing and items through config files for this binding ?

Try messing around following snipper

Thing lswlogger:LSWLoggerV5:SOMEID "Label" @ "Location" [hostname="ip-to-inverter",  port="8899", serialNumber="YOUR-LOGGER-SN",  refreshTime="120", retriesCount="10" , maxOfflineTime="2160"]

This is becase, thing has following config

		<config-description>
			<parameter name="hostname" type="text" required="true">
				<context>network-address</context>
				<label>Logger Hostname/IP</label>
				<description>Hostname or IP address of the device.</description>
			</parameter>
			<parameter name="port" type="integer" min="1" required="true">
				<label>Port</label>
				<description>TCP port of the device.</description>
				<default>8899</default>
			</parameter>
			<parameter name="serialNumber" type="integer" required="true">
				<context></context>
				<label>Logger serial number</label>
				<description>Logger serial number, those with format 17xxxx are confirmed to be supported.</description>
			</parameter>
			<parameter name="refreshTime" type="integer" required="true">
				<label>Refresh time</label>
				<description>Interval the device is polled in sec.</description>
				<default>120</default>
			</parameter>
			<parameter name="retriesCount" type="integer">
				<label>Number of retries</label>
				<description>Number of connecting retries before giving up and assuming that logger is offline</description>
				<default>10</default>
			</parameter>
			<parameter name="maxOfflineTime" type="integer">
				<label>maxOfflineTime</label>
				<description>Maximum time (in minutes) in which logger is assumed to be offline</description>
				<default>2160</default>
			</parameter>
		</config-description>

Thanks!

Tried connecting my datalogger but unfortunately it doesn’t work.
I have SN 19xxxxxxx and firmware ME-121001-V1.0.6

Binding is probably incompatible with your binding :frowning:

It seems that your stick may not be supported. You may ask the vendor to update the firmware remotely (you can email sofar for that) and make another try.

Supported sticks:
wifi stick with firmware - LSW3_15_FFFF_1.0.57, those have serial number starting with 17xxx
wifi stick with firmware - LSW3_15_FFFF_1.0.65, those have serial number starting with 17xxx
eth stick with firmware - ME_08_2701_2.06, those have serial number starting with 21xxx

For being sure you may make logs more verbose:
log:set debug org.openhab.binding.lswlogger

And recheck

My invertor was installed last week, and last minute we switched the Solis invertor for a Sofar, but the backend stuff seems to be the same!

My thing comes online and the channel online also reports on.
Other channels remains empty, but it’s dark now, so maybe no data is coming through now…?

I see one warning in the logs:
2021-12-10 23:16:58.102 [WARN ] [al.protocolv5.UnknownResponseHandler] - Unknown response from logger dcb03cb0e4, length 46: A513001015000D1A65E26902018418040074020000E0B8AF6101900200036615A501001047000E1A65E269003015

Device serial number 177xxxxxxx
Firmware version LSW3_15_270A_1.32

I have a wifi logger atm, but will be changed to an ethernet version.

Hi,

Did you manage to check the binding during a day?

Vesrion 1.32 may be v4 protocol which I have not implemented in my binding.

Piotr

hi , yes during the day I receive the same warnings and no other channels are updated.

edit: I have received the LAN logger and will try to set it up today to see which firmware and protocol it uses.

edit: @Piotr_Bojko I installed the eth logger, buth same message
Device serial number 210xxxxxxx
Firmware version ME_0C_270A_1.05

2021-12-16 21:32:13.766 [WARN ] [al.protocolv5.UnknownResponseHandler] - Unknown response from logger ceca6e271c, length 9: A5170010450300B502

@Piotr_Bojko are you still working on this binding?

Hi, currently I have heave workload at work, so no - I don’t do anything with the binding. It may last few weeks until I would have some time to improve it.

I will put the binding to the markeplace then, or maybe to the official listing for binding in OH.

In meantime, and because I had no time to check projects like Sofar2mqtt, I created a rule which gets the data from the cloud (not my preferred way, but it works…)

The only thing you need is access to the API, you can request this by sending a mail to customerservice@solarmanpv.com

I created a lot of items, and a rule which is triggered every 5 minutes (0 0/5 * * * ? *)

items:

Group  						solarman  									"Solarman"                              <energy> (General)  ["Sensor"]

String 						solarman_token 								"Bearer token" 				    		<energy> (solarman) ["Sensor"]
String 						solarman_SN 								"SN" 									<energy> (solarman) ["Sensor"]
String 						solarman_HardwareVersion 					"Hardware Version" 						<energy> (solarman) ["Sensor"]
String 						solarman_MasterSoftwareVersion 				"Master Software Version" 				<energy> (solarman) ["Sensor"]
String 						solarman_ViceSoftwareVersion 				"Vice Software Version" 				<energy> (solarman) ["Sensor"]
String 						solarman_StandardMainVersion 				"Standard Main Version" 				<energy> (solarman) ["Sensor"]
String 						solarman_CommunicationCPUSoftwareVersion	"Communication CPU Software Version"	<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_DCVoltagePV1 						"DC Voltage PV1" 						<energy> (solarman) ["Sensor"] {stateDescription="unit"[pattern="%.1f %unit%"]}
Number:ElectricPotential 	solarman_DCVoltagePV2 						"DC Voltage PV2" 						<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_DCCurrentPV1 						"DC Current PV1" 						<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_DCCurrentPV2 						"DC Current PV2" 						<energy> (solarman) ["Sensor"]
Number:Power 				solarman_DCPowerPV1 						"DC Power PV1" 							<energy> (solarman) ["Sensor"]
Number:Power 				solarman_DCPowerPV2 						"DC Power PV2" 							<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_ACVoltageRUA 						"AC Voltage R/U/A" 						<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_ACCurrentRUA 						"AC Current R/U/A" 						<energy> (solarman) ["Sensor"]
Number:Power 				solarman_TotalACOutputPower 				"Total AC Output Power" 				<energy> (solarman) ["Sensor"]
Number:Frequency 			solarman_ACOutputFrequencyR 				"AC Output Frequency R" 				<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_OutputVoltageOffgridRphase 		"Output Voltage Off-grid--R phase" 		<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_CumulativeProductionActive 		"Cumulative Production (Active)" 		<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_DailyProductionActive 				"Daily Production (Active)" 			<energy> (solarman) ["Sensor"]
Number:Power 				solarman_ActivePowerPVExt 					"ActivePower PV Ext" 					<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_DcComponentPhaseRCurrent 			"Dc Component- Phase R Current" 		<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_DcComponentPhaseRVoltage 			"Dc Component- Phase R Voltage" 		<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_PpcAcCurrentR 						"Ppc Ac Current R" 						<energy> (solarman) ["Sensor"]
Number:Power 				solarman_TotalReactivePower 				"Total Reactive Power" 					<energy> (solarman) ["Sensor"]
Number:Power 				solarman_RPhaseLoadRctivePower 				"R-Phase Load Rctive Power" 			<energy> (solarman) ["Sensor"]
String 						solarman_GridStatus 						"Grid Status" 							<energy> (solarman) ["Sensor"]
Number:Frequency 			solarman_GridFrequency 						"Grid Frequency" 						<energy> (solarman) ["Sensor"]
Number:Power 				solarman_TotalGridPower 					"Total Grid Power" 						<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_CumulativeGridFeedin 				"Cumulative Grid Feed-in" 				<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_CumulativeEnergyPurchased 			"Cumulative Energy Purchased" 			<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_DailyGridFeedin 					"Daily Grid Feed-in" 					<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_DailyEnergyPurchased 				"Daily Energy Purchased" 				<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_LeakCurrent 						"Leak Current" 							<energy> (solarman) ["Sensor"]
Number:ElectricResistance 	solarman_InsulationImpedance1 				"Insulation Impedance 1" 				<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_NBUSVoltage 						"NBUS Voltage" 							<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_ConsumptionCurrentRUA 				"Consumption Current R/U/A" 			<energy> (solarman) ["Sensor"]
Number:Power 				solarman_TotalConsumptionPower 				"Total Consumption Power" 				<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_CumulativeConsumption 				"Cumulative Consumption" 				<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_DailyConsumption 					"Daily Consumption" 					<energy> (solarman) ["Sensor"]
String 						solarman_BatteryStatus 						"Battery Status" 						<energy> (solarman) ["Sensor"]
Number:Power 				solarman_BatteryPower 						"Battery Power" 						<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_TotalChargingEnergy 				"Total Charging Energy" 				<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_TotalDischargingEnergy 			"Total Discharging Energy" 				<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_DailyChargingEnergy 				"Daily Charging Energy" 				<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_DailyDischargingEnergy 			"Daily Discharging Energy" 				<energy> (solarman) ["Sensor"]
Number:Power 				solarman_PowerBatteryPack1 					"Power Battery Pack 1" 					<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_TotalVoltageBatteryPack1 			"Total Voltage Battery Pack 1" 			<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_CurrentBatteryPack1 				"Current- Battery Pack 1" 				<energy> (solarman) ["Sensor"]
Number:Dimensionless 		solarman_SoCBatteryPack1 					"SoC- Battery Pack 1" 					<energy> (solarman) ["Sensor"] {stateDescription="unit"[pattern="%.1f %unit%"]}
Number 						solarman_CycleTimesBatteryPack1 			"Cycle Times-Battery Pack1" 			<energy> (solarman) ["Sensor"]
Number:Temperature 			solarman_TemperatureBatteryPack1 			"Temperature- Battery Pack 1" 			<energy> (solarman) ["Sensor"]
Number:Temperature 			solarman_ModuleTemperature 					"Module Temperature" 					<energy> (solarman) ["Sensor"]
Number:Temperature 			solarman_SinglePlateAmbientTemperature 		"Single Plate Ambient Temperature" 		<energy> (solarman) ["Sensor"]
Number:Temperature 			solarman_RadiatorTemperature1 				"Radiator Temperature 1" 				<energy> (solarman) ["Sensor"]
DateTime 					solarman_SystemTime 						"System Time" 							<energy> (solarman) ["Sensor"] {stateDescription="date"[pattern="%1$td-%1$tm-%1$tY %1$tH:%1$tM"]}
Number:ElectricCurrent 		solarman_BalancedCurrent 					"Balanced Current" 						<energy> (solarman) ["Sensor"]
Number:Time 				solarman_GenerationTimeToday 				"Generation Time Today" 				<energy> (solarman) ["Sensor"]
Number:Time 				solarman_GenerationTimeTotal 				"Generation Time Total" 				<energy> (solarman) ["Sensor"]
Number:Time 				solarman_TotalRunningHour 					"Total Running Hour" 					<energy> (solarman) ["Sensor"]
Number 						solarman_BootCountdown 						"Boot Countdown" 						<energy> (solarman) ["Sensor"]
Number:ElectricCurrent 		solarman_BuckCurrent 						"Buck Current" 							<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_BusVoltage 						"Bus Voltage" 							<energy> (solarman) ["Sensor"]
Number:ElectricPotential 	solarman_PBUSVoltage 						"PBUS Voltage" 							<energy> (solarman) ["Sensor"]
String 						solarman_Inverterstatus 					"Inverter status" 						<energy> (solarman) ["Sensor"]
Number:Energy 				solarman_CumulativeSelfUsed 				"Cumulative Self Used" 					<energy> (solarman) ["Sensor"]

rule:
You first need some things:

curl --request POST \
  --url 'https://api.solarmanpv.com/account/v1.0/token?appId=<appId>&language=en&=' \
  --header 'Content-Type: application/json' \
  --data '{
	"appSecret": "<appSecret>",
	"email": "<email>",
	"password": "<passwordConvertedToSHA256>"
}'

I also scheduled a rule to obtain a new token every 30 days (token expires after 60 days) (0 0 8 1 * ? *)

val appId = "***********"
val appSecret = "***********"
val email = "***********"
val pwd = "***********"
val url = "https://api.solarmanpv.com/account/v1.0/token?appId=" + appId + "&language=en"
             
val content = '{"appSecret": "' + appSecret + '", "email": "' + email + '", "password": "' + pwd + '"}'
val headers = newHashMap("Content-Type" -> "application/json")
  
var String request  = sendHttpPostRequest(url, "application/json", content, headers, 3000)
logInfo("Solarman", "update token")
val token = transform("JSONPATH", "$..access_token", request)
//logInfo("Solarman", "token : " + token)
solarman_token.postUpdate(token)

Main rule:

val deviceSn = "************"
val url = "https://api.solarmanpv.com/device/v1.0/currentData?language=en&="
val token = solarman_token.state 
val content = '{"deviceSn": "' + deviceSn + '"}'
val headers = newHashMap("Authorization" -> "Bearer " + token)
var String request  = sendHttpPostRequest(url, "application/json", content, headers, 3000)   
val keys = transform("JSONPATH", "$..dataList..key", request)
val data = transform("JSONPATH", "$..dataList", request)
val Integer dataCount = Integer::parseInt(transform("JSONPATH", "$..dataList.length()", data))

var Integer j = 0  
while (j < dataCount) {
  var String dataKey = transform("JSONPATH", "$[" + j +"].key", data)
  var String dataValue = transform("JSONPATH", "$[" + j +"].value", data)
  var String dataUnit = transform("JSONPATH", "$[" + j +"].unit", data)
  var String dataName = transform("JSONPATH", "$[" + j +"].name", data)
  var String itemName = "solarman_" + dataName.replace(" ", "").replace("-","").replace("/","").replace("(","").replace(")","").replace("Single Pla","SinglePla")
  itemName.postUpdate(dataValue)  
    
  j++
}

This rule loops trough all received data and adds the values to the items. Maybe not the best way, but it works fine for me since 2 months :slight_smile:

Hello.

Thanks for your work.

I’ve got an Error in the rule, can you help me ?

2022-02-19 10:45:00.877 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID ‘MyRules_Solar-1’ failed: For input string: “{“code”:null,“msg”:null,“success”:true,“requestId”:“bf43c57e43985f58”,“deviceSn”:“4033847309”,“deviceId”:203084057,“deviceType”:“COLLECTOR”,“deviceState”:1,“dataList”:[{“key”:“SN1”,“value”:“4033847309”,“unit”:null,“name”:“Embedded Device SN”},{“key”:“MDUv1”,“value”:“MW3_14_5406_1.29”,“unit”:null,“name”:“Module Version No”},{“key”:“WORK_TIM1”,“value”:“1”,“unit”:“s”,“name”:“Total running time”},{“key”:“OFFSET_TIM1”,“value”:“1645263763”,“unit”:“s”,“name”:“Offset time”},{“key”:“SEND_PERIOD1”,“value”:“5”,“unit”:“Min”,“name”:“Data Uploading Period”},{“key”:“COLLECT_PERIOD1”,“value”:“60”,“unit”:“s”,“name”:“Data Acquisition Period”},{“key”:“max_conn_n1”,“value”:“1”,“unit”:null,“name”:“Max. No. of Connected Devices”},{“key”:“SGits1”,“value”:“6”,“unit”:null,“name”:“Signal Strength”},{“key”:“HEA_F1”,“value”:“120”,“unit”:“s”,“name”:“Heart Rate”}]}” in MyRules_Solar

Thanks for help !

Best regards

Hendrik

Is this all you get from the logs? Because you have a different device, you obviously need other items, did you create them?

ps: I added a second script to update the bearer token every month (expires after 60 days)

Okay, thanks for your fast answer.

There are not more logging Informations.

The rule crashed in the line:

val Integer dataCount = Integer::parseInt(transform(“JSONPATH”, “$…dataList.length()”, data))

I don’t know why…

I tryed the transform(“JSONPATH”, “$…dataList.length()”, data)
Without an error.

So that’s why the error cames from the parseInt?

Any idea ?

What’s the content of the request variable? Did you already tested the HTTP calls in a program like Postman?
We need to make sure the response you get is a correct json output.

Thanks for your work, it helped me a lot accessing solarman cloud data using the solarman API.
One Question: How did you manage the units (variable dataUnit)? I needed to write the units manually one by one into the stateDescription. Did you find another solution using dataUnit?
Next steps: I also would like to access my data w/o using the cloud. My device is a “Bosswerk MI600” microinverter, which seems to be identical to DEYE SUN600. Included is a datalogger and a WLAN-unit sending the data into the solarman cloud. I did some sniffing of the network traffic using wirkshark and I think, someday I will be successful in reading the data directly.
My Firmware version: MW3_15_5406_1.35

Piotr, thanks a lot for your work. Direct access is not working for me:

The lswlogger is online, but openhab.log shows error (debug on):

2022-02-27 11:42:40.750 [WARN ] [al.protocolv5.UnknownResponseHandler] - Unknown response from logger 04487b6e8e, length 29: A510001015007B907C31F2020100B01B00922300002AA5FF6106009715
2022-02-27 11:44:40.752 [DEBUG] [internal.protocolv5.LswLoggerHandler] - Ticking org.openhab.binding.lswlogger.internal.protocolv5.states.SendingRequestState@7d8e6f
2022-02-27 11:44:40.757 [DEBUG] [internal.protocolv5.LswLoggerHandler] - Ticking org.openhab.binding.lswlogger.internal.protocolv5.states.ReadingResponseState@178580c
2022-02-27 11:44:40.761 [WARN ] [al.protocolv5.UnknownResponseHandler] - Unknown response from logger 04487b6e8e, length 43: A510001015007E907C31F2020176B01B00092400002AA5FF6106008815A501001047007F907C31F2000615
2022-02-27 11:44:40.763 [DEBUG] [ternal.protocolv5.ResponseDispatcher] - Handler class org.openhab.binding.lswlogger.internal.protocolv5.UnknownResponseHandler matched response
2022-02-27 11:44:40.764 [DEBUG] [internal.protocolv5.LswLoggerHandler] - Ticking org.openhab.binding.lswlogger.internal.protocolv5.states.WaitingForNextSendRequestState@1bfa413

Inverter: Bosswerk MI600 (lieke DEYE SUN600)
Firmware version: MW3_15_5406_1.35
S/N: 2106xxxxxx

Perhaps I’ll try to extract the data using LAN sniffing having a network logger in between.
Any idea?