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]
}