I have also a WebConnect with only the “SMA Portal”. I don’t have the right to enable Mobius so I looked on how to extract the data directly from SMA Portal as my feed to there is working fine.
I have this under test right now. This is a python script to be executed under “AppDaemon” integration.
Don’t forget to replace xxxxxx & yyyyyy with your credential.
Probably still some debugging needed (I don’t know yet how long I can keep active my session, I try a check every 5 min and a logon once per hour.
from bs4 import BeautifulSoup
import requests
import json
import time
from datetime import datetime
import appdaemon.plugins.hass.hassapi as hass
class sma_bridge(hass.Hass):
def initialize(self):
self.log("Initialize sma_bridge")
self._loginskip = 0
self._handler5 = None
self.run_at_sunset( self.sunset, offset = -30 * 60)
self.run_at_sunrise(self.sunrise, offset = 30 * 60)
if self.sun_up():
self.sunrise("")
else:
self.set_state("sensor.sma_power",state = 0)
self.log("End sma_bridge")
def sunrise(self, kwargs):
self.log("sma_bridge: Sunrise")
tickstart = datetime.now()
if self._handler5 == None:
self._handler5 = self.run_every(self.run_every_c, tickstart, 5 * 60)
def sunset(self,kwargs):
self.log("sma_bridge: Sunset")
if self._handler5 != None:
self.cancel_timer(self._handler5)
self._handler5 = None
self.set_state("sensor.sma_power",state = 0)
def run_every_c(self, kwargs):
myj = self.getSMAData()
power = str(myj["PV"])
if power.isnumeric():
old_state = self.get_state(entity="sensor.sma_power")
if old_state != power:
self.set_state("sensor.sma_power",state = power, attributes = myj)
else:
self._session = None
# For login: logout https://www.sunnyportal.com/Templates/Start.aspx?logout=true
# To Fetch data: https://www.sunnyportal.com/Dashboard
# Example 1: {"__type":"LiveDataUI","Timestamp":{"__type":"DateTime","DateTime":"2020-04-10T09:27:00","Kind":"Unspecified"},"PV":2688,"FeedIn":null,"GridConsumption":null,"DirectConsumption":null,"SelfConsumption":null,"SelfSupply":null,"TotalConsumption":null,"DirectConsumptionQuote":null,"SelfConsumptionQuote":null,"AutarkyQuote":null,"BatteryIn":null,"BatteryOut":null,"BatteryChargeStatus":null,"OperationHealth":{"__type":"StateOfHealth","Ok":1,"Warning":0,"Error":0,"Unknown":0},"BatteryStateOfHealth":null,"ModuleTemperature":null,"EnvironmentTemperature":null,"WindSpeed":null,"Insolation":null,"BatteryMode":null,"InfoMessages":[],"WarningMessages":[],"ErrorMessages":[],"Info":{}}
# Example 2: {"__type":"LiveDataUI","Timestamp":{"__type":"DateTime","DateTime":"2020-04-10T09:47:04","Kind":"Unspecified"},"PV":null,"FeedIn":null,"GridConsumption":null,"DirectConsumption":null,"SelfConsumption":null,"SelfSupply":null,"TotalConsumption":null,"DirectConsumptionQuote":null,"SelfConsumptionQuote":null,"AutarkyQuote":null,"BatteryIn":null,"BatteryOut":null,"BatteryChargeStatus":null,"OperationHealth":null,"BatteryStateOfHealth":null,"ModuleTemperature":null,"EnvironmentTemperature":null,"WindSpeed":null,"Insolation":null,"BatteryMode":null,"InfoMessages":[],"WarningMessages":[],"ErrorMessages":[],"Info":{}}
#############################
#
# SMA Portal
#
#############################
def _Login(self):
if self._loginskip > 0 and self._session != None:
self._loginskip -=1
else:
self._loginskip = 12
session = requests.session()
url ="https://www.sunnyportal.com/Templates/Start.aspx?logout=true&ReturnUrl=%2fFixedPages%2fDashboard.aspx"
response = session.get(url)
myhtml = response.content
soup = BeautifulSoup(myhtml,'html.parser')
x = soup.find("input", {"id" : "__VIEWSTATE"} )
viewstate = x['value']
x = soup.find("input", {"id" : "__VIEWSTATEGENERATOR"} )
viewstategenerator = x['value']
parameters= {
'__EVENTTARGET':'',
'__EVENTARGUMENT':'',
'__VIEWSTATE':viewstate,
'__VIEWSTATEGENERATOR':viewstategenerator,
'ctl00$ContentPlaceHolder1$Logincontrol1$txtUserName':'[email protected]',
'ctl00$ContentPlaceHolder1$Logincontrol1$txtPassword':'yyyyyyyyyyyyyyyyy',
'ctl00$ContentPlaceHolder1$Logincontrol1$LoginBtn':'Login',
'ctl00$ContentPlaceHolder1$Logincontrol1$RedirectURL':'',
'ctl00$ContentPlaceHolder1$Logincontrol1$RedirectPlant':'',
'ctl00$ContentPlaceHolder1$Logincontrol1$RedirectPage':'',
'ctl00$ContentPlaceHolder1$Logincontrol1$RedirectDevice':'',
'ctl00$ContentPlaceHolder1$Logincontrol1$RedirectOther':'',
'ctl00$ContentPlaceHolder1$Logincontrol1$PlantIdentifier':'',
'ctl00$ContentPlaceHolder1$Logincontrol1$ServiceAccess':'true',
'ctl00$ContentPlaceHolder1$Logincontrol1$MemorizePassword':'on',
'ClientScreenWidth':2048,
'ClientScreenHeight':1152,
'ClientScreenAvailWidth':2048,
'ClientScreenAvailHeight':1112,
'ClientWindowInnerWidth':2048,
'ClientWindowInnerHeight':1010,
'ClientBrowserVersion':65,
'ctl00$ContentPlaceHolder1$hiddenLanguage':'en-us'
}
myheaders = {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9,fr;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
response = session.post(url,parameters,headers=myheaders)
if response.status_code == 200:
self.log ("SMA Logon :" + str(response.status_code))
self._session = session
else:
self.log("Error SMA Login:" + str(response.status_code))
self._session = None
return self._session
def getSMAData(self):
session = self._Login()
url="https://www.sunnyportal.com/Dashboard"
myheaders = {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9,fr;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
response = session.get(url,headers=myheaders)
if response.status_code == 200:
myhtml = response.content
myj = json.loads(myhtml)
self.log("getSMAData:" + str(myj))
else:
self.log ("Error SMA getSMAData:" + myhtml)
myj = {"PV": "Error"}
return myj