Skip to content

Commit

Permalink
Added extra sensor and more streamlining.
Browse files Browse the repository at this point in the history
Added an extra sensor, the total energy per panel.

Reduced the amount of API calls, it was 4 per panel, but not is has been reduced to 1.
  • Loading branch information
ProudElm committed Oct 18, 2022
1 parent 00f50fa commit 8e0bb36
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 54 deletions.
13 changes: 6 additions & 7 deletions custom_components/solaredgeoptimizers/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@

_LOGGER = logging.getLogger(__name__)


STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required("siteid"): str,
vol.Required("username"): str,
vol.Required("password"): str,
}
)
{
vol.Required("siteid"): str,
vol.Required("username"): str,
vol.Required("password"): str,
}
)


class SolarEdgeWebAuth:
Expand Down
2 changes: 2 additions & 0 deletions custom_components/solaredgeoptimizers/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
SENSOR_TYPE_OPT_VOLTAGE = "Optimizer_voltage"
SENSOR_TYPE_POWER = "Power"
SENSOR_TYPE_VOLTAGE = "Voltage"
SENSOR_TYPE_ENERGY = "Lifetime_energy"
SENSOR_TYPE = [
SENSOR_TYPE_CURRENT,
SENSOR_TYPE_OPT_VOLTAGE,
SENSOR_TYPE_POWER,
SENSOR_TYPE_VOLTAGE,
SENSOR_TYPE_ENERGY,
]
2 changes: 1 addition & 1 deletion custom_components/solaredgeoptimizers/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"@proudelm"
],
"iot_class": "cloud_polling",
"version": "1.0.4"
"version": "1.1.0"
}
111 changes: 82 additions & 29 deletions custom_components/solaredgeoptimizers/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Platform for sensor integration."""
from homeassistant.helpers.entity import DeviceInfo
import json

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand All @@ -15,6 +16,7 @@
POWER_WATT,
ELECTRIC_POTENTIAL_VOLT,
ELECTRIC_CURRENT_AMPERE,
ENERGY_KILO_WATT_HOUR,
)

from .solaredgeoptimizers import (
Expand All @@ -34,13 +36,16 @@
SENSOR_TYPE_CURRENT,
SENSOR_TYPE_POWER,
SENSOR_TYPE_VOLTAGE,
SENSOR_TYPE_ENERGY,
)
import logging

SCAN_INTERVAL = UPDATE_DELAY

_LOGGER = logging.getLogger(__name__)

temp_paneel = None


async def async_setup_entry(
hass: HomeAssistant,
Expand All @@ -51,6 +56,8 @@ async def async_setup_entry(
# Add the needed sensors to hass
client = hass.data[DOMAIN][entry.entry_id][DATA_API_CLIENT]

# entry.data["paneel"] = "boe"

# panelen = await hass.async_add_executor_job(client.requestAllData)
site = await hass.async_add_executor_job(client.requestListOfAllPanels)

Expand All @@ -76,15 +83,15 @@ async def async_setup_entry(
client.requestSystemData, optimizer.optimizerId
)
if info is not None:
for sensortype in SENSOR_TYPE:
async_add_entities(
[
SolarEdgeOptimizersSensor(
client, entry, info, sensortype, optimizer
)
],
update_before_add=False,
)
for sensortype in SENSOR_TYPE:
async_add_entities(
[
SolarEdgeOptimizersSensor(
hass, client, entry, info, sensortype, optimizer
)
],
update_before_add=False,
)

_LOGGER.info(
"Done adding all optimizers. Now adding sensors, this may take some time!"
Expand All @@ -98,12 +105,14 @@ class SolarEdgeOptimizersSensor(SensorEntity):

def __init__(
self,
hass: HomeAssistant,
client: solaredgeoptimizers,
entry: ConfigEntry,
paneel: SolarEdgeOptimizerData,
sensortype,
optimizer: SolarlEdgeOptimizer,
) -> None:
self._hass = hass
self._client = client
self._entry = entry
self._paneelobject = paneel
Expand All @@ -129,6 +138,9 @@ def __init__(
elif self._sensor_type is SENSOR_TYPE_POWER:
self._attr_native_unit_of_measurement = POWER_WATT
self._attr_device_class = SensorDeviceClass.POWER
elif self._sensor_type is SENSOR_TYPE_ENERGY:
self._attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR
self._attr_device_class = SensorDeviceClass.ENERGY

@property
def device_info(self):
Expand All @@ -146,27 +158,68 @@ def device_info(self):

def update(self):
"""ddd"""
paneel_info = ""

try:
paneel_info = self._client.requestSystemData(self._paneelobject.paneel_id)
except Exception as err:
_LOGGER.error(
"Error updating data for panel: %s", self._paneelobject.paneel_id
)
raise err

# print(paneel_info)
# {'Current [A]': '7.47', 'Optimizer Voltage [V]': '39.75', 'Power [W]': '253.00', 'Voltage [V]': '33.88'}
global temp_paneel

waarde = ""

if self._sensor_type is SENSOR_TYPE_VOLTAGE:
waarde = paneel_info.voltage
elif self._sensor_type is SENSOR_TYPE_CURRENT:
waarde = paneel_info.current
elif self._sensor_type is SENSOR_TYPE_OPT_VOLTAGE:
waarde = paneel_info.optimizer_voltage
elif self._sensor_type is SENSOR_TYPE_POWER:
waarde = paneel_info.power
# Voor totaal energie moeten we wat anders doen
if self._sensor_type is SENSOR_TYPE_ENERGY:
# waarde ophalen
lifetimeenergy = json.loads(self._client.getLifeTimeEnergy())
waarde = (
float(
lifetimeenergy[str(self._paneelobject.paneel_id)]["unscaledEnergy"]
)
) / 1000
else:

try:

if temp_paneel == None:
paneel_info = self._client.requestSystemData(
self._paneelobject.paneel_id
)
temp_paneel = paneel_info
_LOGGER.info(
"Paneel data opgehaald, geheugen was leeg. Paneel {}".format(
self._paneelobject.paneel_desciption
)
)
elif temp_paneel.paneel_id is not self._paneelobject.paneel_id:
paneel_info = self._client.requestSystemData(
self._paneelobject.paneel_id
)
temp_paneel = paneel_info
_LOGGER.info(
"Paneel data opgehaald, verkeerd paneel in geheugen. Paneel {}".format(
self._paneelobject.paneel_desciption
)
)
else:
paneel_info = temp_paneel
_LOGGER.info("Paneel data uit geheugen opgehaald")

except Exception as err:
_LOGGER.error(
"Error updating data for panel: %s", self._paneelobject.paneel_id
)
raise err

# print(paneel_info)
# {'Current [A]': '7.47', 'Optimizer Voltage [V]': '39.75', 'Power [W]': '253.00', 'Voltage [V]': '33.88'}

waarde = ""

if paneel_info is not None:
if self._sensor_type is SENSOR_TYPE_VOLTAGE:
waarde = paneel_info.voltage
elif self._sensor_type is SENSOR_TYPE_CURRENT:
waarde = paneel_info.current
elif self._sensor_type is SENSOR_TYPE_OPT_VOLTAGE:
waarde = paneel_info.optimizer_voltage
elif self._sensor_type is SENSOR_TYPE_POWER:
waarde = paneel_info.power
else:
return

self._attr_native_value = waarde
132 changes: 115 additions & 17 deletions custom_components/solaredgeoptimizers/solaredgeoptimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import json
from jsonfinder import jsonfinder
import logging
from requests import Session

_LOGGER = logging.getLogger(__name__)

# from requests.auth import HTTPBasicAuth
Expand Down Expand Up @@ -53,11 +55,15 @@ def requestSystemData(self, itemId):

if r.status_code == 200:
json_object = decodeResult(r.text)
if json_object["lastMeasurementDate"] == "":
_LOGGER.info("Skipping optimizer %s without measurements", itemId)
return None
else:
return SolarEdgeOptimizerData(itemId, json_object)
try:
if json_object["lastMeasurementDate"] == "":
_LOGGER.info("Skipping optimizer %s without measurements", itemId)
return None
else:
return SolarEdgeOptimizerData(itemId, json_object)
except Exception as errortje:
_LOGGER.error(errortje)
return None
else:
print("Fout bij verzenden. Status code: {}".format(r.status_code))
print(r.text)
Expand All @@ -73,10 +79,84 @@ def requestAllData(self):
for optimizer in string.optimizers:
info = self.requestSystemData(optimizer.optimizerId)
if info is not None:
data.append(info)
data.append(info)

return data

def getLifeTimeEnergy(self):
session = Session()
session.head(
"https://monitoring.solaredge.com/solaredge-apigw/api/sites/1871534/layout/energy"
)

url = "https://monitoring.solaredge.com/solaredge-web/p/login"

session.auth = (self.username, self.password)

# request a login url the get the correct cookie
r1 = session.get(url)

# Fix the cookie to get a string.
therightcookie = self.MakeStringFromCookie(session.cookies.get_dict())
# The csrf-token is needed as a seperate header.
thecrsftoken = self.GetThecsrfToken(session.cookies.get_dict())

# Build up the request.
response = session.post(
url="https://monitoring.solaredge.com/solaredge-apigw/api/sites/1871534/layout/energy?timeUnit=ALL",
headers={
"authority": "monitoring.solaredge.com",
"accept": "*/*",
"accept-language": "en-US,en;q=0.9,nl;q=0.8",
"content-type": "application/json",
"cookie": therightcookie,
"origin": "https://monitoring.solaredge.com",
"referer": "https://monitoring.solaredge.com/solaredge-web/p/site/1871534/",
"sec-ch-ua": '"Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"x-csrf-token": thecrsftoken,
"x-kl-ajax-request": "Ajax_Request",
"x-requested-with": "XMLHttpRequest",
},
)

if response.status_code == 200:
return response.text
else:
return "ERROR - HTTP CODE: {}".format(response.status_code)

def GetThecsrfToken(self, cookies):
for cookie in cookies:
if cookie == "CSRF-TOKEN":
return cookies[cookie]

def MakeStringFromCookie(self, cookies):

maincookiestring = ""
for cookie in cookies:
if cookie == "CSRF-TOKEN":
maincookiestring = (
maincookiestring + cookie + "=" + cookies[cookie] + ";"
)
elif cookie == "JSESSIONID":
maincookiestring = (
maincookiestring + cookie + "=" + cookies[cookie] + ";"
)

maincookiestring = (
maincookiestring
+ "SolarEdge_Locale=nl_NL; SolarEdge_Locale=nl_NL; solaredge_cookie_concent=1;SolarEdge_Field_ID={}".format(
self.siteid
)
)

return maincookiestring


def decodeResult(result):
json_result = ""
Expand Down Expand Up @@ -198,18 +278,36 @@ class SolarEdgeOptimizerData:
"""boe"""

def __init__(self, paneelid, json_object):
self._json_obj = json_object

# Atributen die we willen zien:
self.serialnumber = json_object["serialNumber"]
self.paneel_id = paneelid
self.paneel_desciption = json_object["description"]
self.lastmeasurement = json_object["lastMeasurementDate"]
self.model = json_object["model"]
self.manufacturer = json_object["manufacturer"]
self.serialnumber = ""
self.paneel_id = ""
self.paneel_desciption = ""
self.lastmeasurement = ""
self.model = ""
self.manufacturer = ""

# Waarden
self.current = json_object["measurements"]["Current [A]"]
self.optimizer_voltage = json_object["measurements"]["Optimizer Voltage [V]"]
self.power = json_object["measurements"]["Power [W]"]
self.voltage = json_object["measurements"]["Voltage [V]"]
self.current = ""
self.optimizer_voltage = ""
self.power = ""
self.voltage = ""

if paneelid is not None:
self._json_obj = json_object

# Atributen die we willen zien:
self.serialnumber = json_object["serialNumber"]
self.paneel_id = paneelid
self.paneel_desciption = json_object["description"]
self.lastmeasurement = json_object["lastMeasurementDate"]
self.model = json_object["model"]
self.manufacturer = json_object["manufacturer"]

# Waarden
self.current = json_object["measurements"]["Current [A]"]
self.optimizer_voltage = json_object["measurements"][
"Optimizer Voltage [V]"
]
self.power = json_object["measurements"]["Power [W]"]
self.voltage = json_object["measurements"]["Voltage [V]"]

0 comments on commit 8e0bb36

Please sign in to comment.