SAJ 3 phase Solar inverter (beta)

Hi,

I just wanted to post my code I made for polling data from a SAJ 3 phase solar inverter.
I didn’t have time to finish some things, so if there is an interest and somebody solves the issues let me know.
Known issues are:

  • price calculation doesn’t work
  • the refresh doesnt work automatically. Namely at dusk the inverter goes to sleep so the polling stops. you need to press refresh the next day to see the status.

Note that I have very basic programming skills and any code improvements are welcome

/**
 *  SAJ Solar System
 *
 *  Copyright 2018 Darko Gjorgjievski based on original versions by swap-file
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software 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.
 *
 */
 

preferences {
	input("deviceIP", "string", title:"IP Address", description: "IP Address", required: true, displayDuringSetup: true)
	input("price", "Double", title:"eur/kWh", description: "electricity price", required: false, displayDuringSetup: true)
}

metadata {
	// Automatically generated. Make future change here.
	definition (name: "SAJ 3 phase solar inverter", author: "swap-file") {
		capability "Energy Meter"
		capability "Polling"
		capability "Refresh"
		capability "Sensor"
		capability "Power Meter"
       
	}

	// UI tile definitions
	tiles (scale: 2){
		
		valueTile(	"power", "device.power", width: 6, height:4) 		{
			state(	"device.power",label:'${currentValue} W', backgroundColors:[
			[value: 500, color: "#bc2323"],
			[value: 1500, color: "#d04e00"],
			[value: 3000, color: "#f1d801"],
			[value: 4500, color: "#44b621"],
			[value: 6000, color: "#90d2a7"],
			[value: 7500, color: "#1e9cbb"],
			[value: 9000, color: "#153591"]
			]
			)
		}
		
		valueTile(	"voltage1", "device.voltage1",width: 2,  height: 1 ) {
			state("device.voltage1", label:'V1 ${currentValue}V' )
		}
		
		valueTile(	"voltage2", "device.voltage2",width: 2,  height: 1 ) {
			state("device.voltage2", label:'V2 ${currentValue}V' )
		}
		
		valueTile(	"voltage3", "device.voltage3",width: 2,  height: 1 ) {
			state("device.voltage3", label:'V3 ${currentValue}V' )
		}
		
		valueTile(	"voltage_DC1", "device.voltage_DC1",width: 2,  height: 1 ) {
			state("device.voltage_DC1", label:'Vdc1 ${currentValue}V' )
		}
		
		valueTile(	"voltage_DC2", "device.voltage_DC2",width: 2,  height: 1 ) {
			state("device.voltage_DC2", label:'Vdc2 ${currentValue}V' )
		}
		
		valueTile(	"voltage_DCbus", "device.voltage_DCbus",width: 2,  height: 1 ) {
			state("device.voltage_DCbus", label:'Vdc ${currentValue}V' )
		}
		
		valueTile(	"current1", "device.current1",width: 2,  height: 1 ) {
			state("device.current1", label:'I1 ${currentValue}A' )
		}
		
		valueTile(	"current2", "device.current2",width: 2,  height: 1 ) {
			state("device.current2", label:'I2 ${currentValue}A' )
		}
		
		valueTile(	"current3", "device.current3",width: 2,  height: 1 ) {
			state("device.current3", label:'I3 ${currentValue}A' )
		}
        
		valueTile(	"current_DC1", "device.current_DC1",width: 2,  height: 1 ) {
			state("device.current_DC1", label:'Idc1 ${currentValue}A' )
		}
		
		valueTile(	"current_DC2", "device.current_DC2",width: 2,  height: 1 ) {
			state("device.current_DC2", label:'Idc2 ${currentValue}A' )
		}
		
		valueTile(	"power1", "device.power1",width: 2,  height: 1 ) {
			state("device.power1", label:'P1 ${currentValue}W' )
		}
		
		valueTile(	"power2", "device.power2",width: 2,  height: 1 ) {
			state("device.power2", label:'P2 ${currentValue}W' )
		}
		
		valueTile(	"power3", "device.power3",width: 2,  height: 1 ) {
			state("device.power3", label:'P3 ${currentValue}W' )
		}
		
		valueTile(	"temperature", "device.temperature",width: 2,  height: 1 ) {
			state("device.temperature", label:'t: ${currentValue}°C' )
		}
        
		valueTile(	"CO2", "device.CO2",width: 2,  height: 1 ) {
			state("device.CO2", label:'CO2: ${currentValue}kg' )
		}
		
		valueTile(	"frequency", "device.frequency",width: 2,  height: 1 ) {
			state("device.frequency", label:'f: ${currentValue}Hz' )
		}
		
		valueTile("refresh", "command.refresh",width: 2,  height: 2) {
			state "default", label:'refresh', action:"refresh.refresh", icon:"st.secondary.refresh-icon"
		}
		
		valueTile(	"daily_max_power","device.daily_max_power",width: 4,height: 1) {
			state("device.daily_max_power", label:'Daily Max: ${currentValue}W')
		}
		valueTile(	"daily_total_power","device.daily_total_power",width: 4,height: 1) {
			state("device.daily_total_power", label:'Today: ${currentValue}kWh')
		}	
		valueTile(	"daily_total_time","device.daily_total_time",width: 2,height: 1) {
			state("device.daily_total_time", label:'Today: ${currentValue}h')
		}
		valueTile(	"total_power","device.total_power",width: 4,height: 1) {
			state("device.total_power", label:'Total: ${currentValue}kWh')
		}	
		valueTile(	"total_time","device.total_time",width: 2,height: 1) {
			state("device.total_time", label:'Total: ${currentValue}h')
		}
		
		valueTile(	"savings", "device.savings",width: 4,  height: 1) {
			state("device.savings", label:'€ '  + price)
		}
		main(["power"])
        
		details(["power","daily_total_power", "daily_total_time", "total_power", "total_time", "daily_max_power", "refresh", "savings", "power1", "power2", "power3", "voltage1", "voltage2", "voltage3", "current1","current2","current3","temperature","CO2", "frequency", "voltage_DC1", "voltage_DC2", "voltage_DCbus", "current_DC1", "current_DC2"])
	}
}

def poll() {
	log.trace 'Poll Called'
	runCmd()
}

def refresh() {
	log.trace 'Refresh Called'
	runCmd()
}

def runCmd() {
	def host = deviceIP
	def hosthex = convertIPtoHex(host).toUpperCase()
	def LocalDevicePort = "80"
	def porthex = convertPortToHex(LocalDevicePort).toUpperCase()
	device.deviceNetworkId = "$hosthex:$porthex"

	log.debug "The device id configured is: $device.deviceNetworkId"

	def headers = [:] 
	headers.put("HOST", "$host:$LocalDevicePort")
	
	log.debug "The Header is $headers"
	
	def path = "/real_time_data.xml"
	def body = ''
	log.debug "Uses which method: $DevicePostGet"
	def method = "GET"

	try {
		log.debug "Making xml request to $device.deviceNetworkId"
		def hubAction = new physicalgraph.device.HubAction(
			method: method,
			path: path,
			body: body,
			headers: headers
		)
		hubAction.options = [outputMsgToS3:false]
		log.debug hubAction
		hubAction
	}
	catch (Exception e) {
		log.debug "Hit Exception $e on $hubAction"
	}
}

private String convertIPtoHex(ipAddress) { 
	String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
	log.debug "IP address entered is $ipAddress and the converted hex code is $hex"
	return hex
}
private String convertPortToHex(port) {
	String hexport = port.toString().format( '%04x', port.toInteger() )
	//log.debug hexport
	return hexport
}

def parse(String description) {
	//this is automatically called when the hub action returns
	log.debug "Got Reply"
	def xml = parseLanMessage(description)
    def respData = new XmlSlurper().parseText(xml.body)
   
    log.debug "body: ${xml.body}"
    log.debug "respData: ${respData}"
       
	def evt1 = createEvent (name: "status", value: (respData.state), unit:"")
    def evt2 = createEvent (name: "voltage1", value: ((respData.Vac_l1).toDouble()).round(1), unit:"V")
    def evt3 = createEvent (name: "voltage2", value: ((respData.Vac_l2).toDouble()).round(1), unit:"V")
    def evt4 = createEvent (name: "voltage3", value: ((respData.Vac_l3).toDouble()).round(1), unit:"V")
    def evt5 = createEvent (name: "current1", value: ((respData.Iac_l1).toDouble()).round(2), unit:"A")
    def evt6 = createEvent (name: "current2", value: ((respData.Iac_l2).toDouble()).round(2), unit:"A")
    def evt7 = createEvent (name: "current3", value: ((respData.Iac_l3).toDouble()).round(2), unit:"A")
    def evt8 = createEvent (name: "frequency", value: ((((respData.Freq1).toDouble()+(respData.Freq2).toDouble()+(respData.Freq3).toDouble())/3).toDouble()).round(2), unit:"Hz")	
	def evt9 = createEvent (name: "power1", value: ((respData.pac1).toDouble()).round(1), unit:"W")
    def evt10 = createEvent (name: "power2", value: ((respData.pac2).toDouble()).round(1), unit:"W")
    def evt11 = createEvent (name: "power3", value: ((respData.pac3).toDouble()).round(1), unit:"W")
	def evt12 = createEvent (name: "power", value: ((respData.'p-ac').toDouble()).round(1), unit:"W")
	def evt13 = createEvent (name: "temperature", value: ((respData.temp).toDouble()).round(1), unit:"oC")
	def evt14 = createEvent (name: "daily_total_power", value: ((respData.'e-today').toDouble() ).round(2), unit:"kWh") 
	def evt15 = createEvent (name: "daily_total_time", value: ((respData.'t-today').toDouble() ).round(1), unit:"h") 
	def evt16 = createEvent (name: "total_power", value: ((respData.'e-total').toDouble()).round(2), unit:"kWh")
	def evt17 = createEvent (name: "total_time", value: ((respData.'t-total').toDouble()).round(1), unit:"h")
	def evt18 = createEvent (name: "CO2", value: ((respData.CO2).toDouble()).round(0), unit:"kg")
    def evt19 = createEvent (name: "voltage_DC1", value: ((respData.'v-pv1').toDouble()).round(1), unit:"V")
    def evt20 = createEvent (name: "voltage_DC2", value: ((respData.'v-pv2').toDouble()).round(1), unit:"V")
    def evt21 = createEvent (name: "voltage_DCbus", value: ((respData.'v-bus').toDouble()).round(1), unit:"V")
	def evt22 = createEvent (name: "daily_max_power", value: ((respData.maxPower).toDouble()).round(1), unit:"W")
	def evt23 = createEvent (name: "current_DC1", value: ((respData.'i-pv11').toDouble()), unit:"V")
    def evt24 = createEvent (name: "current_DC2", value: ((respData.'i-pv21').toDouble()), unit:"V")
	//state totalE =(respData.'e-total').toDouble()
    return [evt1,evt2, evt3, evt4, evt5, evt6, evt7, evt8, evt9, evt10,evt11,evt12, evt13, evt14, evt15, evt16, evt17, evt18, evt19, evt20, evt21,evt22, evt23 , evt24]
	
}

Hi:

Some news about this?

I try to use it, but doesnt work.

I must to specify the user and pass… no where where to input that…

Thanks!

Hi

I don’t need to enter user/password on mine.
I just transferred the code from domoticz to smartthings

I also recently changed the cide to enable calculations of savings

Hi D_Gjorgjievski:

Do you have some new release?

Dear Pablo

No, I don’t have any new release.
The automatic refresh is still an issue, but I mentioned I have very basic programming skills so I couldn’t fix it.
All the rest is working