Closed santiagozky closed 1 year ago
I managed to make it work temporarily by:
The same is happening to me. My plant is gone in the "old platform".
I managed to make it work temporarily by:
* replacing the domain in the sensor.py * forcing it not to verify SSL certificates (seems like the cert chain is broken and HASS rejects it): session = async_create_clientsession(hass,False).
Hola puede explicarlo mejor, es que no lo pillo gracias
@teamMOYA is your kit branded as SAJ or some other white label (eg. Greenheiss)?
Hi, the exact same thing is happening to me but I did what you said and it still doesn't work. Can you explain exactly what needs to be changed? I have GreenHeiss. Thanks!
@teamMOYA is your kit branded as SAJ or some other white label (eg. Greenheiss)?
Mine are rebranded as "Solarprofit", but they don't have a web like Saj and Greenheiss. I'll have to wait for the new web access update.
I managed to make it work temporarily by:
- replacing the domain in the sensor.py
- forcing it not to verify SSL certificates (seems like the cert chain is broken and HASS rejects it): session = async_create_clientsession(hass,False).
Where do you change the domain to inside sensor.py? Do you change https://fop.saj-electric.com/saj to https://esaj-home.saj-electric.com/ or something?
Hello, I also had that problem.
My Greenheiss h1 inverter. I use an old integration 1.3.1 which I already have working.
Right now everything works perfectly for me
replacing the domain in the sensor.py
I leave a copy of my sensor.py, in case it helps you, but remember that I use 1.3.1
""" Alternative for the SAJ local API sensor. Unfortunally there is no public api. This Sensor will read the private api of the eSolar portal at https://fop.saj-electric.com/ """
import asyncio
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from datetime import timedelta
import datetime
import calendar
from functools import reduce
import logging
from typing import Final
import aiohttp
import async_timeout
import voluptuous as vol
import re
from bs4 import BeautifulSoup
from homeassistant.components.sensor import (
PLATFORM_SCHEMA,
STATE_CLASS_TOTAL_INCREASING,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import (
CONF_RESOURCES,
CONF_USERNAME,
CONF_PASSWORD,
CONF_SENSORS,
CONF_DEVICE_ID,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
ENERGY_KILO_WATT_HOUR,
POWER_WATT,
PERCENTAGE,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle, dt
def add_months(sourcedate, months):
month = sourcedate.month - 1 + months
year = sourcedate.year + month // 12
month = month % 12 + 1
day = min(sourcedate.day, calendar.monthrange(year,month)[1])
return datetime.date(year, month, day)
def add_years(d, years):
try:
return d.replace(year = d.year + years)
except ValueError:
return d + (date(d.year + years, 1, 1) - date(d.year, 1, 1))
BASE_URL = 'https://inversores-style.greenheiss.com/cloud/login'
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=3)
SENSOR_PREFIX = 'esolar '
ATTR_MEASUREMENT = "measurement"
ATTR_SECTION = "section"
SENSOR_LIST = {
"nowPower",
"runningState",
"devOnlineNum",
"todayElectricity",
"monthElectricity",
"yearElectricity",
"totalElectricity",
"todayGridIncome",
"income",
"lastUploadTime",
"totalPlantTreeNum",
"totalReduceCo2",
"todayAlarmNum",
"plantuid",
"plantname",
"currency",
"address",
"isOnline",
"status",
"peakPower",
#sec & h1
"pvElec",
"useElec",
"buyElec",
"sellElec",
"buyRate",
"sellRate",
"selfUseRate",
"totalBuyElec",
"totalConsumpElec",
"totalSellElec",
"selfConsumedRate1",
"selfConsumedRate2",
"selfConsumedEnergy1",
"selfConsumedEnergy2",
"plantTreeNum",
"reduceCo2",
"totalGridPower",
"totalLoadPower",
"totalPvgenPower",
"totalPvEnergy",
"totalLoadEnergy",
"totalBuyEnergy",
"totalSellEnergy",
#h1
"chargeElec",
"dischargeElec",
"batCapcity",
"isAlarm",
"batCurr",
"batEnergyPercent",
"batteryDirection",
"batteryPower",
"gridDirection",
"gridPower",
"h1Online",
"outPower",
"outPutDirection",
"pvDirection",
"pvPower",
"solarPower",
#h1s2
"h1s2_pv_volt_1",
"h1s2_pv_current_1",
"h1s2_pv_power_1",
"h1s2_pv_volt_2",
"h1s2_pv_current_2",
"h1s2_pv_power_2",
"h1s2_pv_volt_3",
"h1s2_pv_current_3",
"h1s2_pv_power_3",
"h1s2_bat_volt",
"h1s2_bat_current",
"h1s2_bat_power",
"h1s2_bat_total_charge",
"h1s2_bat_total_discharge",
"h1s2_load_power",
"h1s2_grid_volt_1",
"h1s2_grid_current_1",
"h1s2_grid_power_1",
"h1s2_grid_volt_2",
"h1s2_grid_current_2",
"h1s2_grid_power_2",
"h1s2_grid_volt_3",
"h1s2_grid_current_3",
"h1s2_grid_power_3",
}
SENSOR_TYPES: Final[tuple[SensorEntityDescription]] = (
SensorEntityDescription(
key="nowPower",
name="nowPower",
icon="mdi:solar-power",
native_unit_of_measurement=POWER_WATT,
device_class=DEVICE_CLASS_POWER,
),
SensorEntityDescription(
key="runningState",
name="runningState",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="devOnlineNum",
name="devOnlineNum",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="todayElectricity",
name="todayElectricity",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="monthElectricity",
name="monthElectricity",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="yearElectricity",
name="yearElectricity",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="totalElectricity",
name="totalElectricity",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="selfUseRate",
name="selfUseRate",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="totalBuyElec",
name="totalBuyElec",
icon="mdi:solar-panel",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="totalConsumpElec",
name="totalConsumpElec",
icon="mdi:solar-panel",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="totalSellElec",
name="totalSellElec",
icon="mdi:solar-panel",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="todayGridIncome",
name="todayGridIncome",
icon="mdi:currency-eur",
),
SensorEntityDescription(
key="income",
name="income",
icon="mdi:currency-eur",
),
SensorEntityDescription(
key="lastUploadTime",
name="lastUploadTime",
icon="mdi:timer-sand",
),
SensorEntityDescription(
key="totalPlantTreeNum",
name="totalPlantTreeNum",
icon="mdi:tree",
),
SensorEntityDescription(
key="totalReduceCo2",
name="totalReduceCo2",
icon="mdi:molecule-co2",
),
SensorEntityDescription(
key="todayAlarmNum",
name="todayAlarmNum",
icon="mdi:alarm",
),
SensorEntityDescription(
key="plantuid",
name="plantuid",
icon="mdi:api",
),
SensorEntityDescription(
key="plantname",
name="plantname",
icon="mdi:api",
),
SensorEntityDescription(
key="currency",
name="currency",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="address",
name="address",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="isOnline",
name="isOnline",
icon="mdi:api",
),
SensorEntityDescription(
key="status",
name="status",
icon="mdi:api",
),
SensorEntityDescription(
key="peakPower",
name="peakPower",
icon="mdi:solar-panel",
native_unit_of_measurement=POWER_WATT,
device_class=DEVICE_CLASS_POWER,
),
SensorEntityDescription(
key="pvElec",
name="pvElec",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="useElec",
name="useElec",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="buyElec",
name="buyElec",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="sellElec",
name="sellElec",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="buyRate",
name="buyRate",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="sellRate",
name="sellRate",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="selfConsumedRate1",
name="selfConsumedRate1",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="selfConsumedRate2",
name="selfConsumedRate2",
icon="mdi:solar-panel",
),
SensorEntityDescription(
key="selfConsumedEnergy1",
name="selfConsumedEnergy1",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="selfConsumedEnergy2",
name="selfConsumedEnergy2",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
),
SensorEntityDescription(
key="plantTreeNum",
name="plantTreeNum",
icon="mdi:tree",
),
SensorEntityDescription(
key="reduceCo2",
name="reduceCo2",
icon="mdi:molecule-co2",
),
SensorEntityDescription(
key="totalGridPower",
name="totalGridPower",
icon="mdi:solar-panel",
native_unit_of_measurement=POWER_WATT,
),
SensorEntityDescription(
key="totalLoadPower",
name="totalLoadPower",
icon="mdi:solar-panel",
native_unit_of_measurement=POWER_WATT,
device_class=DEVICE_CLASS_POWER,
),
SensorEntityDescription(
key="totalPvgenPower",
name="totalPvgenPower",
icon="mdi:solar-panel",
native_unit_of_measurement=POWER_WATT,
),
SensorEntityDescription(
key="totalPvEnergy",
name="totalPvEnergy",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="totalLoadEnergy",
name="totalLoadEnergy",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="totalBuyEnergy",
name="totalBuyEnergy",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="totalSellEnergy",
name="totalSellEnergy",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
#h1
SensorEntityDescription(
key="batCapcity",
name="batCapcity",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A·h"
),
SensorEntityDescription(
key="isAlarm",
name="isAlarm",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="batCurr",
name="batCurr",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="batEnergyPercent",
name="batEnergyPercent",
icon="mdi:solar-panel-large",
native_unit_of_measurement=PERCENTAGE,
),
SensorEntityDescription(
key="batteryDirection",
name="batteryDirection",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="batteryPower",
name="batteryPower",
icon="mdi:solar-panel-large",
native_unit_of_measurement=POWER_WATT,
),
SensorEntityDescription(
key="gridPower",
name="gridPower",
icon="mdi:solar-panel-large",
native_unit_of_measurement=POWER_WATT,
),
SensorEntityDescription(
key="h1Online",
name="h1Online",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="outPower",
name="outPower",
icon="mdi:solar-panel-large",
native_unit_of_measurement=POWER_WATT,
),
SensorEntityDescription(
key="outPutDirection",
name="outPutDirection",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="pvPower",
name="pvPower",
icon="mdi:solar-panel-large",
native_unit_of_measurement=POWER_WATT,
),
SensorEntityDescription(
key="solarPower",
name="solarPower",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="chargeElec",
name="chargeElec",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
SensorEntityDescription(
key="dischargeElec",
name="dischargeElec",
icon="mdi:solar-panel-large",
native_unit_of_measurement=ENERGY_KILO_WATT_HOUR,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
#h1s2
SensorEntityDescription(
key="h1s2_pv_volt_1",
name="h1s2_pv_volt_1",
icon="mdi:solar-panel-large",
native_unit_of_measurement="V"
),
SensorEntityDescription(
key="h1s2_pv_current_1",
name="h1s2_pv_current_1",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="h1s2_pv_power_1",
name="h1s2_pv_power_1",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
SensorEntityDescription(
key="h1s2_pv_volt_2",
name="h1s2_pv_volt_2",
icon="mdi:solar-panel-large",
native_unit_of_measurement="V"
),
SensorEntityDescription(
key="h1s2_pv_current_2",
name="h1s2_pv_current_2",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="h1s2_pv_power_2",
name="h1s2_pv_power_2",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
SensorEntityDescription(
key="h1s2_pv_volt_3",
name="h1s2_pv_volt_3",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="h1s2_pv_current_3",
name="h1s2_pv_current_3",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="h1s2_pv_power_3",
name="h1s2_pv_power_3",
icon="mdi:solar-panel-large",
),
SensorEntityDescription(
key="h1s2_bat_volt",
name="h1s2_bat_volt",
icon="mdi:solar-panel-large",
native_unit_of_measurement="V"
),
SensorEntityDescription(
key="h1s2_bat_current",
name="h1s2_bat_current",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="h1s2_bat_power",
name="h1s2_bat_power",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
SensorEntityDescription(
key="h1s2_bat_total_charge",
name="h1s2_bat_total_charge",
icon="mdi:solar-panel-large",
native_unit_of_measurement="Ah"
),
SensorEntityDescription(
key="h1s2_bat_total_discharge",
name="h1s2_bat_total_discharge",
icon="mdi:solar-panel-large",
native_unit_of_measurement="Ah"
),
SensorEntityDescription(
key="h1s2_load_power",
name="h1s2_load_power",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
SensorEntityDescription(
key="h1s2_grid_volt_1",
name="h1s2_grid_volt_1",
icon="mdi:solar-panel-large",
native_unit_of_measurement="V"
),
SensorEntityDescription(
key="h1s2_grid_current_1",
name="h1s2_grid_current_1",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="h1s2_grid_power_1",
name="h1s2_grid_power_1",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
SensorEntityDescription(
key="h1s2_grid_volt_2",
name="h1s2_grid_volt_2",
icon="mdi:solar-panel-large",
native_unit_of_measurement="V"
),
SensorEntityDescription(
key="h1s2_grid_current_2",
name="h1s2_grid_current_2",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="h1s2_grid_power_2",
name="h1s2_grid_power_2",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
SensorEntityDescription(
key="h1s2_grid_volt_3",
name="h1s2_grid_volt_3",
icon="mdi:solar-panel-large",
native_unit_of_measurement="V"
),
SensorEntityDescription(
key="h1s2_grid_current_3",
name="h1s2_grid_current_3",
icon="mdi:solar-panel-large",
native_unit_of_measurement="A"
),
SensorEntityDescription(
key="h1s2_grid_power_3",
name="h1s2_grid_power_3",
icon="mdi:solar-panel-large",
native_unit_of_measurement="W"
),
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_RESOURCES, default=list(SENSOR_LIST)): vol.All(
cv.ensure_list, [vol.In(SENSOR_LIST)]
),
vol.Optional(CONF_SENSORS, default="None"): cv.string,
vol.Optional(CONF_DEVICE_ID, default="None"): cv.string,
}
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Setup the SAJ eSolar sensors."""
session = async_create_clientsession(hass,False)
data = SAJeSolarMeterData(session, config.get(CONF_USERNAME), config.get(CONF_PASSWORD), config.get(CONF_SENSORS), config.get(CONF_DEVICE_ID))
await data.async_update()
entities = []
for description in SENSOR_TYPES:
if description.key in config[CONF_RESOURCES]:
sensor = SAJeSolarMeterSensor(description, data, config.get(CONF_SENSORS))
entities.append(sensor)
async_add_entities(entities, True)
return True
class SAJeSolarMeterData(object):
"""Handle eSolar object and limit updates."""
def __init__(self, session, username, password, sensors, sec_sn):
"""Initialize the data object."""
self._session = session
self._url = BASE_URL
self.username = username
self.password = password
self.sensors = sensors
self.sec_sn = sec_sn
self._data = None
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self):
"""Download and update data from SAJeSolar."""
try:
today = datetime.date.today()
clientDate = today.strftime('%Y-%m-%d')
# Login to eSolar API
url = 'https://inversores-style.greenheiss.com/cloud/login'
payload = {
'lang': 'en',
'username': self.username,
'password': self.password,
'rememberMe': 'true'
}
headers_login = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': 'org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE=en; op_esolar_lang=en',
'DNT': '1',
'Host': 'inversores-style.greenheiss.com',
'Origin': 'https://inversores-style.greenheiss.com',
'Referer': 'https://inversores-style.greenheiss.com/cloud/login',
'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
'sec-ch-ua-mobile': '?0',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent'
: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
}
response = await self._session.post(url, headers=headers_login, data=payload)
if response.status != 200:
_LOGGER.error(f"{response.url} returned {response.status}")
return
# Get API Plant info from Esolar Portal
url2 = 'https://inversores-style.greenheiss.com/cloud/monitor/site/getUserPlantList'
headers = {
'Connection': 'keep-alive',
'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'DNT': '1',
'X-Requested-With': 'XMLHttpRequest',
'sec-ch-ua-mobile': '?0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Origin': 'https://inversores-style.greenheiss.com',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': 'https://inversores-style.greenheiss.com/cloud/monitor/home/index',
'Accept-Language': 'nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7'
}
payload2= f"pageNo=&pageSize=&orderByIndex=&officeId=&clientDate={clientDate}&runningState=&selectInputType=1&plantName=&deviceSn=&type=&countryCode=&isRename=&isTimeError=&systemPowerLeast=&systemPowerMost="
response2 = await self._session.post(url2, headers=headers, data=payload2)
if response2.status != 200:
_LOGGER.error(f"{response2.url} returned {response2.status}")
return
plantInfo = await response2.json()
plantuid = plantInfo['plantList'][0]['plantuid']
# Get API Plant Solar Details
url3 = "https://inversores-style.greenheiss.com/cloud/monitor/site/getPlantDetailInfo"
payload3= f"plantuid={plantuid}&clientDate={clientDate}"
response3 = await self._session.post(url3, headers=headers, data=payload3)
if response3.status != 200:
_LOGGER.error(f"{response3.url} returned {response3.status}")
return
plantDetails = await response3.json()
plantDetails.update(plantInfo)
# getPlantDetailChart2
plantuid = plantDetails['plantList'][0]['plantuid']
deviceSnArr = plantDetails['plantDetail']['snList'][0]
previousChartDay = today - timedelta(days=1)
nextChartDay = today + timedelta(days = 1)
chartDay = today.strftime('%Y-%m-%d')
previousChartMonth = add_months(today,-1).strftime('%Y-%m')
nextChartMonth = add_months(today, 1).strftime('%Y-%m')
chartMonth = today.strftime('%Y-%m')
previousChartYear = add_years(today, -1).strftime('%Y')
nextChartYear = add_years(today, 1).strftime('%Y')
chartYear = today.strftime('%Y')
epochmilliseconds = round(int((datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1)).total_seconds() * 1000))
url4 = f"https://inversores-style.greenheiss.com/cloud/monitor/site/getPlantDetailChart2?plantuid={plantuid}&chartDateType=1&energyType=0&clientDate={clientDate}&deviceSnArr={deviceSnArr}&chartCountType=2&previousChartDay={previousChartDay}&nextChartDay={nextChartDay}&chartDay={chartDay}&previousChartMonth={previousChartMonth}&nextChartMonth={nextChartMonth}&chartMonth={chartMonth}&previousChartYear={previousChartYear}&nextChartYear={nextChartYear}&chartYear={chartYear}&elecDevicesn=&_={epochmilliseconds}"
response4 = await self._session.post(url4, headers=headers)
if response4.status != 200:
_LOGGER.error(f"{response4.url} returned {response4.status}")
return
plantcharts = await response4.json()
plantDetails.update(plantcharts)
# H1 Module
if self.sensors == "h1":
# getStoreOrAcDevicePowerInfo
url_getStoreOrAcDevicePowerInfo = f"https://inversores-style.greenheiss.com/cloud/monitor/site/getStoreOrAcDevicePowerInfo?plantuid=&devicesn={deviceSnArr}&_={epochmilliseconds}"
response_getStoreOrAcDevicePowerInfo = await self._session.post(url_getStoreOrAcDevicePowerInfo, headers=headers)
if response_getStoreOrAcDevicePowerInfo.status != 200:
_LOGGER.error(f"{response_getStoreOrAcDevicePowerInfo.url} returned {response_getStoreOrAcDevicePowerInfo.status}")
return
result_getStoreOrAcDevicePowerInfo = await response_getStoreOrAcDevicePowerInfo.json()
plantDetails.update(result_getStoreOrAcDevicePowerInfo)
_LOGGER.debug(result_getStoreOrAcDevicePowerInfo)
url5 = f"https://inversores-style.greenheiss.com/cloud/cloudMonitor/deviceInfo?devicesn={deviceSnArr}"
response5 = await self._session.get(url5, headers=headers)
if response5.status != 200:
_LOGGER.error(f"{response5.url} returned {response5.status}")
html = await response5.text()
soup = BeautifulSoup(html, "lxml")
span = soup.select(".real_num span")
h1s2= {
"h1s2": {
"h1s2_pv_volt_1":re.sub(r'[^0-9.]', '', (span[0].text).split("/")[0]),
"h1s2_pv_current_1":re.sub(r'[^0-9.]', '', (span[0].text).split("/")[1]),
"h1s2_pv_power_1":re.sub(r'[^0-9.]', '', (span[0].text).split("/")[2]),
"h1s2_pv_volt_2":re.sub(r'[^0-9.]', '', (span[1].text).split("/")[0]),
"h1s2_pv_current_2":re.sub(r'[^0-9.]', '', (span[1].text).split("/")[1]),
"h1s2_pv_power_2":re.sub(r'[^0-9.]', '', (span[1].text).split("/")[2]),
"h1s2_pv_volt_3":re.sub(r'[^0-9.]', '', (span[2].text).split("/")[0]),
"h1s2_pv_current_3":re.sub(r'[^0-9.]', '', (span[2].text).split("/")[1]),
"h1s2_pv_power_3":re.sub(r'[^0-9.]', '', (span[2].text).split("/")[2]),
"h1s2_bat_volt":re.sub(r'[^0-9.]', '', (span[3].text).split("/")[0]),
"h1s2_bat_current":re.sub(r'[^0-9.\-]', '', (span[3].text).split("/")[1]),
"h1s2_bat_power":re.sub(r'[^0234567891.\-]', '', (span[3].text).split("/")[2]),
"h1s2_bat_total_charge":re.sub(r'[^0-9.]', '', (span[4].text).split("/")[0]),
"h1s2_bat_total_discharge":re.sub(r'[^0-9.]', '', (span[4].text).split("/")[1]),
"h1s2_load_power":re.sub(r'[^0-9.]', '', span[5].text),
"h1s2_grid_volt_1":re.sub(r'[^0-9.]', '', (span[6].text).split("/")[0]),
"h1s2_grid_current_1":re.sub(r'[^0-9.]', '', (span[6].text).split("/")[1]),
"h1s2_grid_power_1":re.sub(r'[^0-9.]', '', (span[6].text).split("/")[2]),
"h1s2_grid_volt_2":re.sub(r'[^0-9.]', '', (span[7].text).split("/")[0]),
"h1s2_grid_current_2":re.sub(r'[^0-9.]', '', (span[7].text).split("/")[1]),
"h1s2_grid_power_2":re.sub(r'[^0-9.]', '', (span[7].text).split("/")[2]),
"h1s2_grid_volt_3":re.sub(r'[^0-9.]', '', (span[8].text).split("/")[0]),
"h1s2_grid_current_3":re.sub(r'[^0-9.]', '', (span[8].text).split("/")[1]),
"h1s2_grid_power_3":re.sub(r'[^0-9.]', '', (span[8].text).split("/")[2]),
"h1s2_pv_volt_1":re.sub(r'[^0-9.]', '', (span[0].text).split("/")[0]),
"h1s2_pv_current_1":re.sub(r'[^0-9.]', '', (span[0].text).split("/")[1]),
"h1s2_pv_power_1":re.sub(r'[^0-9.]', '', (span[0].text).split("/")[2]),
"h1s2_pv_volt_2":re.sub(r'[^0-9.]', '', (span[1].text).split("/")[0]),
"h1s2_pv_current_2":re.sub(r'[^0-9.]', '', (span[1].text).split("/")[1]),
"h1s2_pv_power_2":re.sub(r'[^0-9.]', '', (span[1].text).split("/")[2]),
"h1s2_pv_volt_3":re.sub(r'[^0-9.]', '', (span[2].text).split("/")[0]),
"h1s2_pv_current_3":re.sub(r'[^0-9.]', '', (span[2].text).split("/")[1]),
"h1s2_pv_power_3":re.sub(r'[^0-9.]', '', (span[2].text).split("/")[2]),
"h1s2_bat_volt":re.sub(r'[^0-9.]', '', (span[3].text).split("/")[0]),
"h1s2_bat_current":re.sub(r'[^0-9.]', '', (span[3].text).split("/")[1]),
"h1s2_bat_power":re.sub(r'[^0-9.]', '', (span[3].text).split("/")[2]),
"h1s2_bat_total_charge":re.sub(r'[^0-9.]', '', (span[4].text).split("/")[0]),
"h1s2_bat_total_discharge":re.sub(r'[^0-9.]', '', (span[4].text).split("/")[1]),
"h1s2_load_power":re.sub(r'[^0-9.]', '', span[5].text),
"h1s2_grid_volt_1":re.sub(r'[^0-9.]', '', (span[6].text).split("/")[0]),
"h1s2_grid_current_1":re.sub(r'[^0-9.]', '', (span[6].text).split("/")[1]),
"h1s2_grid_power_1":re.sub(r'[^0-9.]', '', (span[6].text).split("/")[2]),
"h1s2_grid_volt_2":re.sub(r'[^0-9.]', '', (span[7].text).split("/")[0]),
"h1s2_grid_current_2":re.sub(r'[^0-9.]', '', (span[7].text).split("/")[1]),
"h1s2_grid_power_2":re.sub(r'[^0-9.]', '', (span[7].text).split("/")[2]),
"h1s2_grid_volt_3":re.sub(r'[^0-9.]', '', (span[8].text).split("/")[0]),
"h1s2_grid_current_3":re.sub(r'[^0-9.]', '', (span[8].text).split("/")[1]),
"h1s2_grid_power_3":re.sub(r'[^0-9.]', '', (span[8].text).split("/")[2])
}
}
plantDetails.update(h1s2)
elif self.sensors == "None":
self._data = plantDetails
else:
# Data = plantdetails
self._data = plantDetails
# Sec module
if self.sensors == "saj_sec":
# getPlantMeterModuleList
url_module = "https://inversores-style.greenheiss.com/cloud/saj/cloudmonitor/plantMeterModule/getPlantMeterModuleList"
payload_module = f"pageNo=&pageSize=&plantUid={plantuid}"
response_module = await self._session.post(url_module, headers=headers, data=payload_module)
if response_module.status != 200:
_LOGGER.error(f"{response_module.url} returned {response_module.status}")
return
getPlantMeterModuleList = await response_module.json()
temp_getPlantMeterModuleList = dict()
temp_getPlantMeterModuleList["getPlantMeterModuleList"] = getPlantMeterModuleList
plantDetails.update(temp_getPlantMeterModuleList)
moduleSn = plantDetails["getPlantMeterModuleList"]['moduleList'][0]['moduleSn']
# -Debug- Sec module serial number
_LOGGER.debug(moduleSn)
# findDevicePageList
url_findDevicePageList = "https://inversores-style.greenheiss.com/cloud/cloudMonitor/device/findDevicePageList"
payload_findDevicePageList = f"officeId=1&pageNo=&pageSize=&orderName=1&orderType=2&plantuid={plantuid}&deviceStatus=&localDate={chartMonth}&localMonth={chartMonth}"
response_findDevicePageList = await self._session.post(url_findDevicePageList, headers=headers, data=payload_findDevicePageList)
if response_findDevicePageList.status != 200:
_LOGGER.error(f"{response_findDevicePageList.url} returned {response_findDevicePageList.status}")
return
findDevicePageList = await response_findDevicePageList.json()
temp_findDevicePageList = dict()
temp_findDevicePageList["findDevicePageList"] = findDevicePageList
plantDetails.update(temp_findDevicePageList)
# getPlantMeterDetailInfo
url_getPlantMeterDetailInfo = "https://inversores-style.greenheiss.com/cloud/monitor/site/getPlantMeterDetailInfo"
payload_getPlantMeterDetailInfo = f"plantuid={plantuid}&clientDate={clientDate}"
response_getPlantMeterDetailInfo = await self._session.post(url_getPlantMeterDetailInfo, headers=headers, data=payload_getPlantMeterDetailInfo)
if response_getPlantMeterDetailInfo.status != 200:
_LOGGER.error(f"{response_getPlantMeterDetailInfo.url} returned {response_getPlantMeterDetailInfo.status}")
return
getPlantMeterDetailInfo = await response_getPlantMeterDetailInfo.json()
temp_getPlantMeterDetailInfo = dict()
temp_getPlantMeterDetailInfo["getPlantMeterDetailInfo"] = getPlantMeterDetailInfo
plantDetails.update(temp_getPlantMeterDetailInfo)
# getPlantMeterEnergyPreviewInfo
url_getPlantMeterEnergyPreviewInfo = f"https://inversores-style.greenheiss.com/cloud/monitor/site/getPlantMeterEnergyPreviewInfo?plantuid={plantuid}&moduleSn={moduleSn}&_={epochmilliseconds}"
response_getPlantMeterEnergyPreviewInfo = await self._session.get(url_getPlantMeterEnergyPreviewInfo, headers=headers)
if response_getPlantMeterEnergyPreviewInfo.status != 200:
_LOGGER.error(f"{response_getPlantMeterEnergyPreviewInfo.url} returned {response_getPlantMeterEnergyPreviewInfo.status}")
return
getPlantMeterEnergyPreviewInfo = await response_getPlantMeterEnergyPreviewInfo.json()
temp_getPlantMeterEnergyPreviewInfo = dict()
temp_getPlantMeterEnergyPreviewInfo["getPlantMeterEnergyPreviewInfo"] = getPlantMeterEnergyPreviewInfo
plantDetails.update(temp_getPlantMeterEnergyPreviewInfo)
# Get Sec Meter details
url_getPlantMeterChartData = f"https://inversores-style.greenheiss.com/cloud/monitor/site/getPlantMeterChartData?plantuid={plantuid}&chartDateType=1&energyType=0&clientDate={clientDate}&deviceSnArr=&chartCountType=2&previousChartDay={previousChartDay}&nextChartDay={nextChartDay}&chartDay={chartDay}&previousChartMonth={previousChartMonth}&nextChartMonth={nextChartMonth}&chartMonth={chartMonth}&previousChartYear={previousChartYear}&nextChartYear={nextChartYear}&chartYear={chartYear}&moduleSn={moduleSn}&_={epochmilliseconds}"
response_getPlantMeterChartData = await self._session.post(url_getPlantMeterChartData, headers=headers)
if response_getPlantMeterChartData.status != 200:
_LOGGER.error(f"{response_getPlantMeterChartData.url} returned {response_getPlantMeterChartData.status}")
return
getPlantMeterChartData = await response_getPlantMeterChartData.json()
temp_getPlantMeterChartData = dict()
temp_getPlantMeterChartData["getPlantMeterChartData"] = getPlantMeterChartData
plantDetails.update(temp_getPlantMeterChartData)
# Data = plantdetails including Sec module
self._data = plantDetails
elif self.sensors == "None":
self._data = plantDetails
else:
# Data = plantdetails Wtihout Sec module
self._data = plantDetails
# Error logging
except aiohttp.ClientError:
_LOGGER.error("Cannot poll eSolar using url: %s")
return
except asyncio.TimeoutError:
_LOGGER.error("Timeout error occurred while polling eSolar using url: %s")
return
except Exception as err:
_LOGGER.error("Unknown error occurred while polling eSolar: %s", err)
self._data = None
return
# -Debug- Cookies and Data
_LOGGER.debug(self._session.cookie_jar.filter_cookies("https://inversores-style.greenheiss.com"))
_LOGGER.debug(self._data)
# logout session
url_logout = "https://inversores-style.greenheiss.com/cloud/logout"
response_logout = await self._session.post(url_logout, headers=headers)
if response_logout.status != 200:
_LOGGER.error(f"{response_logout.url} returned {response_logout.status}")
return
# Clear session and cookies
self._session.cookie_jar.clear()
self._session.close()
@property
def latest_data(self):
"""Return the latest data object."""
if self._data:
return self._data
_LOGGER.error("return data NONE")
return None
class SAJeSolarMeterSensor(SensorEntity):
"""Collecting data and return sensor entity."""
def __init__(self, description: SensorEntityDescription, data, sensors):
"""Initialize the sensor."""
self.entity_description = description
self._data = data
self._state = None
self.sensors = sensors
self._type = self.entity_description.key
self._attr_icon = self.entity_description.icon
self._attr_name = SENSOR_PREFIX + self.entity_description.name
self._attr_state_class = self.entity_description.state_class
self._attr_native_unit_of_measurement = self.entity_description.native_unit_of_measurement
self._attr_device_class = self.entity_description.device_class
self._attr_unique_id = f"{SENSOR_PREFIX}_{self._type}"
self._discovery = False
self._dev_id = {}
@property
def state(self):
"""Return the state of the sensor. (total/current power consumption/production or total gas used)"""
return self._state
async def async_update(self):
"""Get the latest data and use it to update our sensor state."""
await self._data.async_update()
energy = self._data.latest_data
if energy:
if self._type == 'devOnlineNum':
if 'devOnlineNum' in energy['plantDetail']:
if energy['plantDetail']["devOnlineNum"] is not None:
self._state = int(energy['plantDetail']["devOnlineNum"])
if self._type == 'nowPower':
if 'nowPower' in energy['plantDetail']:
if energy['plantDetail']["nowPower"] is not None:
self._state = float(energy['plantDetail']["nowPower"])
if self._type == 'runningState':
if 'runningState' in energy['plantDetail']:
if energy['plantDetail']["runningState"] is not None:
self._state = int(energy['plantDetail']["runningState"])
if self._type == 'todayElectricity':
if 'todayElectricity' in energy['plantDetail']:
if energy['plantDetail']["todayElectricity"] is not None:
self._state = float(energy['plantDetail']["todayElectricity"])
if self._type == 'monthElectricity':
if 'monthElectricity' in energy['plantDetail']:
if energy['plantDetail']["monthElectricity"] is not None:
self._state = float(energy['plantDetail']["monthElectricity"])
if self._type == 'yearElectricity':
if 'yearElectricity' in energy['plantDetail']:
if energy['plantDetail']["yearElectricity"] is not None:
self._state = float(energy['plantDetail']["yearElectricity"])
if self._type == 'totalElectricity':
if 'totalElectricity' in energy['plantDetail']:
if energy['plantDetail']["totalElectricity"] is not None:
self._state = float(energy['plantDetail']["totalElectricity"])
if self._type == 'todayGridIncome':
if 'todayGridIncome' in energy['plantDetail']:
if energy['plantDetail']["todayGridIncome"] is not None:
self._state = float(energy['plantDetail']["todayGridIncome"])
if self._type == 'income':
if 'income' in energy['plantDetail']:
if energy['plantDetail']["income"] is not None:
self._state = float(energy['plantDetail']["income"])
if self._type == 'selfUseRate':
if 'selfUseRate' in energy['plantDetail']:
if energy['plantDetail']["selfUseRate"] is not None:
self._state = energy['plantDetail']["selfUseRate"]
if self._type == 'totalBuyElec':
if 'totalBuyElec' in energy['plantDetail']:
if energy['plantDetail']["totalBuyElec"] is not None:
self._state = float(energy['plantDetail']["totalBuyElec"])
if self._type == 'totalConsumpElec':
if 'totalConsumpElec' in energy['plantDetail']:
if energy['plantDetail']["totalConsumpElec"] is not None:
self._state = float(energy['plantDetail']["totalConsumpElec"])
if self._type == 'totalSellElec':
if 'totalSellElec' in energy['plantDetail']:
if energy['plantDetail']["totalSellElec"] is not None:
self._state = float(energy['plantDetail']["totalSellElec"])
if self._type == 'todayAlarmNum':
if 'todayAlarmNum' in energy['plantDetail']:
if energy['plantDetail']["todayAlarmNum"] is not None:
self._state = (energy['plantDetail']["todayAlarmNum"])
if self._type == 'todayAlarmNum':
if 'todayAlarmNum' in energy['plantDetail']:
if energy['plantDetail']["todayAlarmNum"] is not None:
self._state = (energy['plantDetail']["todayAlarmNum"])
if self._type == 'lastUploadTime':
if 'lastUploadTime' in energy['plantDetail']:
if energy['plantDetail']["lastUploadTime"] is not None:
self._state = (energy['plantDetail']["lastUploadTime"])
if self._type == 'totalPlantTreeNum':
if 'totalPlantTreeNum' in energy['plantDetail']:
if energy['plantDetail']["totalPlantTreeNum"] is not None:
self._state = (energy['plantDetail']["totalPlantTreeNum"])
if self._type == 'totalReduceCo2':
if 'totalReduceCo2' in energy['plantDetail']:
if energy['plantDetail']["totalReduceCo2"] is not None:
self._state = (energy['plantDetail']["totalReduceCo2"])
if self._type == 'todayAlarmNum':
if 'todayAlarmNum' in energy['plantDetail']:
if energy['plantDetail']["todayAlarmNum"] is not None:
self._state = (energy['plantDetail']["todayAlarmNum"])
if self._type == 'currency':
if 'currency' in energy['plantList'][0]:
if energy['plantList'][0]["currency"] is not None:
self._state = (energy['plantList'][0]["currency"])
if self._type == 'plantuid':
if 'plantuid' in energy['plantList'][0]:
if energy['plantList'][0]["plantuid"] is not None:
self._state = (energy['plantList'][0]["plantuid"])
if self._type == 'plantname':
if 'plantname' in energy['plantList'][0]:
if energy['plantList'][0]["plantname"] is not None:
self._state = (energy['plantList'][0]["plantname"])
if self._type == 'currency':
if 'currency' in energy['plantList'][0]:
if energy['plantList'][0]["currency"] is not None:
self._state = (energy['plantList'][0]["currency"])
if self._type == 'isOnline':
if 'isOnline' in energy['plantList'][0]:
if energy['plantList'][0]["isOnline"] is not None:
self._state = (energy['plantList'][0]["isOnline"])
if self._type == 'address':
if 'address' in energy['plantList'][0]:
if energy['plantList'][0]["address"] is not None:
self._state = (energy['plantList'][0]["address"])
if self._type == 'peakPower':
if 'peakPower' in energy:
if energy["peakPower"] is not None:
self._state = float(energy["peakPower"])
if self._type == 'status':
if 'status' in energy:
if energy["status"] is not None:
self._state = (energy["status"])
########################################################################## SAJ h1
if self.sensors == "h1":
if self._type == 'chargeElec':
if 'chargeElec' in energy['viewBean']:
if energy['viewBean']["chargeElec"] is not None:
self._state = float(energy['viewBean']["chargeElec"])
if self._type == 'dischargeElec':
if 'dischargeElec' in energy['viewBean']:
if energy['viewBean']["dischargeElec"] is not None:
self._state = float(energy['viewBean']["dischargeElec"])
if self._type == 'buyElec':
if 'pvElec' in energy['viewBean']:
if energy['viewBean']["buyElec"] is not None:
self._state = float(energy['viewBean']["buyElec"])
if self._type == 'buyRate':
if 'buyRate' in energy['viewBean']:
if energy['viewBean']["buyRate"] is not None:
self._state = energy['viewBean']["buyRate"]
if self._type == 'pvElec':
if 'pvElec' in energy['viewBean']:
if energy['viewBean']["pvElec"] is not None:
self._state = float(energy['viewBean']["pvElec"])
if self._type == 'selfConsumedEnergy1':
if 'selfConsumedEnergy1' in energy['viewBean']:
if energy['viewBean']["selfConsumedEnergy1"] is not None:
self._state = float(energy['viewBean']["selfConsumedEnergy1"])
if self._type == 'selfConsumedEnergy2':
if 'selfConsumedEnergy2' in energy['viewBean']:
if energy['viewBean']["selfConsumedEnergy2"] is not None:
self._state = float(energy['viewBean']["selfConsumedEnergy2"])
if self._type == 'selfConsumedRate1':
if 'selfConsumedRate1' in energy['viewBean']:
if energy['viewBean']["selfConsumedRate1"] is not None:
self._state = energy['viewBean']["selfConsumedRate1"]
if self._type == 'selfConsumedRate2':
if 'selfConsumedRate2' in energy['viewBean']:
if energy['viewBean']["selfConsumedRate2"] is not None:
self._state = energy['viewBean']["selfConsumedRate2"]
if self._type == 'sellElec':
if 'sellElec' in energy['viewBean']:
if energy['viewBean']["sellElec"] is not None:
self._state = float(energy['viewBean']["sellElec"])
if self._type == 'sellRate':
if 'sellRate' in energy['viewBean']:
if energy['viewBean']["sellRate"] is not None:
self._state = energy['viewBean']["sellRate"]
if self._type == 'useElec':
if 'useElec' in energy['viewBean']:
if energy['viewBean']["useElec"] is not None:
self._state = float(energy['viewBean']["useElec"])
# storeDevicePower
if self._type == 'batCapcity':
if 'batCapcity' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['batCapcity'] is not None:
self._state = float(energy["storeDevicePower"]["batCapcity"])
if self._type == 'isAlarm':
if 'isAlarm' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['isAlarm'] is not None:
self._state = int(energy["storeDevicePower"]["isAlarm"])
if self._type == 'batCurr':
if 'batCurr' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['batCurr'] is not None:
self._state = float(energy["storeDevicePower"]["batCurr"])
if self._type == 'batEnergyPercent':
if 'batEnergyPercent' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['batEnergyPercent'] is not None:
self._state = float(energy["storeDevicePower"]["batEnergyPercent"])
if self._type == 'batteryDirection':
if 'batteryDirection' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['batteryDirection'] is not None:
if energy["storeDevicePower"]["batteryDirection"] == 0:
self._state = "Standby"
elif energy["storeDevicePower"]["batteryDirection"] == 1:
self._state = "Descarga"
elif energy["storeDevicePower"]["batteryDirection"] == -1:
self._state = "Carga"
else:
self._state = f'Unknown: {energy["storeDevicePower"]["batteryDirection"]}'
if self._type == 'batteryPower':
if 'batteryPower' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['batteryPower'] is not None:
self._state = float(energy["storeDevicePower"]["batteryPower"])
if self._type == 'gridDirection':
if 'gridDirection' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['gridDirection'] is not None:
self._state = float(energy["storeDevicePower"]["gridDirection"])
if self._type == 'gridPower':
if 'gridPower' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['gridPower'] is not None:
self._state = float(energy["storeDevicePower"]["gridPower"])
if self._type == 'h1Online':
if 'isOnline' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['isOnline'] is not None:
self._state = int(energy["storeDevicePower"]["isOnline"])
if self._type == 'outPower':
if 'outPower' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['outPower'] is not None:
self._state = float(energy["storeDevicePower"]["outPower"])
if self._type == 'outPutDirection':
if 'outPutDirection' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['outPutDirection'] is not None:
self._state = float(energy["storeDevicePower"]["outPutDirection"])
if self._type == 'pvDirection':
if 'pvDirection' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['pvDirection'] is not None:
self._state = int(energy["storeDevicePower"]["pvDirection"])
if self._type == 'pvPower':
if 'pvPower' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['pvPower'] is not None:
self._state = float(energy["storeDevicePower"]["pvPower"])
if self._type == 'solarPower':
if 'solarPower' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['solarPower'] is not None:
self._state = float(energy["storeDevicePower"]["solarPower"])
if self._type == 'totalLoadPower':
if 'totalLoadPower' in energy["storeDevicePower"]:
if energy["storeDevicePower"]['totalLoadPower'] is not None:
self._state = float(energy["storeDevicePower"]['totalLoadPower'])
########################################################################## CUSTOM SENSORS H1 S2
if self._type == 'h1s2_pv_volt_1':
if 'h1s2_pv_volt_1' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_volt_1'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_volt_1"])
if self._type == 'h1s2_pv_current_1':
if 'h1s2_pv_current_1' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_current_1'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_current_1"])
if self._type == 'h1s2_pv_power_1':
if 'h1s2_pv_power_1' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_power_1'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_power_1"])
if self._type == 'h1s2_pv_volt_2':
if 'h1s2_pv_volt_2' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_volt_2'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_volt_2"])
if self._type == 'h1s2_pv_current_2':
if 'h1s2_pv_current_2' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_current_2'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_current_2"])
if self._type == 'h1s2_pv_power_2':
if 'h1s2_pv_power_2' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_power_2'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_power_2"])
if self._type == 'h1s2_pv_volt_3':
if 'h1s2_pv_volt_3' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_volt_3'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_volt_3"])
if self._type == 'h1s2_pv_current_3':
if 'h1s2_pv_current_3' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_current_3'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_current_3"])
if self._type == 'h1s2_pv_power_3':
if 'h1s2_pv_power_3' in energy["h1s2"]:
if energy["h1s2"]['h1s2_pv_power_3'] is not None:
self._state = float(energy["h1s2"]["h1s2_pv_power_3"])
if self._type == 'h1s2_bat_volt':
if 'h1s2_bat_volt' in energy["h1s2"]:
if energy["h1s2"]['h1s2_bat_volt'] is not None:
self._state = float(energy["h1s2"]["h1s2_bat_volt"])
if self._type == 'h1s2_bat_current':
if 'h1s2_bat_current' in energy["h1s2"]:
if energy["h1s2"]['h1s2_bat_current'] is not None:
self._state = float(energy["h1s2"]["h1s2_bat_current"])
if self._type == 'h1s2_bat_power':
if 'h1s2_bat_power' in energy["h1s2"]:
if energy["h1s2"]['h1s2_bat_power'] is not None:
self._state = float(energy["h1s2"]["h1s2_bat_power"])
if self._type == 'h1s2_bat_total_charge':
if 'h1s2_bat_total_charge' in energy["h1s2"]:
if energy["h1s2"]['h1s2_bat_total_charge'] is not None:
self._state = float(energy["h1s2"]["h1s2_bat_total_charge"])
if self._type == 'h1s2_bat_total_discharge':
if 'h1s2_bat_total_discharge' in energy["h1s2"]:
if energy["h1s2"]['h1s2_bat_total_discharge'] is not None:
self._state = float(energy["h1s2"]["h1s2_bat_total_discharge"])
if self._type == 'h1s2_load_power':
if 'h1s2_load_power' in energy["h1s2"]:
if energy["h1s2"]['h1s2_load_power'] is not None:
self._state = float(energy["h1s2"]["h1s2_load_power"])
if self._type == 'h1s2_grid_volt_1':
if 'h1s2_grid_volt_1' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_volt_1'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_volt_1"])
if self._type == 'h1s2_grid_current_1':
if 'h1s2_grid_current_1' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_current_1'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_current_1"])
if self._type == 'h1s2_grid_power_1':
if 'h1s2_grid_power_1' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_power_1'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_power_1"])
if self._type == 'h1s2_grid_volt_2':
if 'h1s2_grid_volt_2' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_volt_2'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_volt_2"])
if self._type == 'h1s2_grid_current_2':
if 'h1s2_grid_current_2' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_current_2'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_current_2"])
if self._type == 'h1s2_grid_power_2':
if 'h1s2_grid_power_2' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_power_2'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_power_2"])
if self._type == 'h1s2_grid_volt_3':
if 'h1s2_grid_volt_3' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_volt_3'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_volt_3"])
if self._type == 'h1s2_grid_current_3':
if 'h1s2_grid_current_3' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_current_3'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_current_3"])
if self._type == 'h1s2_grid_power_3':
if 'h1s2_grid_power_3' in energy["h1s2"]:
if energy["h1s2"]['h1s2_grid_power_3'] is not None:
self._state = float(energy["h1s2"]["h1s2_grid_power_3"])
########################################################################## Sec module Sensors:
if self.sensors == "saj_sec":
# getPlantMeterChartData - viewBeam
if self._type == 'pvElec':
if 'pvElec' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["pvElec"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["pvElec"])
if self._type == 'useElec':
if 'useElec' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["useElec"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["useElec"])
if self._type == 'buyElec':
if 'buyElec' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["buyElec"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["buyElec"])
if self._type == 'sellElec':
if 'sellElec' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["sellElec"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["sellElec"])
if self._type == 'selfConsumedEnergy1':
if 'selfConsumedEnergy1' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["selfConsumedEnergy1"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["selfConsumedEnergy1"])
if self._type == 'selfConsumedEnergy2':
if 'selfConsumedEnergy2' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["selfConsumedEnergy2"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["selfConsumedEnergy2"])
if self._type == 'reduceCo2':
if 'reduceCo2' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["reduceCo2"] is not None:
self._state = float(energy["getPlantMeterChartData"]['viewBean']["reduceCo2"])
if self._type == 'buyRate':
if 'buyRate' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["buyRate"] is not None:
self._state = (energy["getPlantMeterChartData"]['viewBean']["buyRate"])
if self._type == 'sellRate':
if 'sellRate' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["sellRate"] is not None:
self._state = (energy["getPlantMeterChartData"]['viewBean']["sellRate"])
if self._type == 'selfConsumedRate1':
if 'selfConsumedRate1' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["selfConsumedRate1"] is not None:
self._state = (energy["getPlantMeterChartData"]['viewBean']["selfConsumedRate1"])
if self._type == 'selfConsumedRate2':
if 'selfConsumedRate2' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["selfConsumedRate2"] is not None:
self._state = (energy["getPlantMeterChartData"]['viewBean']["selfConsumedRate2"])
if self._type == 'plantTreeNum':
if 'plantTreeNum' in energy["getPlantMeterChartData"]['viewBean']:
if energy["getPlantMeterChartData"]['viewBean']["plantTreeNum"] is not None:
self._state = (energy["getPlantMeterChartData"]['viewBean']["plantTreeNum"])
# dataCountList
if self._type == 'totalGridPower':
if 'dataCountList' in energy:
if energy["getPlantMeterChartData"]['dataCountList'][4][-1] is not None:
self._state = float(energy["getPlantMeterChartData"]['dataCountList'][3][-1])
if self._type == 'totalLoadPower':
if 'dataCountList' in energy:
if energy["getPlantMeterChartData"]['dataCountList'][4][-1] is not None:
self._state = float(energy["getPlantMeterChartData"]['dataCountList'][2][-1])
if self._type == 'totalPvgenPower':
if 'dataCountList' in energy:
if energy["getPlantMeterChartData"]['dataCountList'][4][-1] is not None:
self._state = float(energy["getPlantMeterChartData"]['dataCountList'][4][-1])
# getPlantMeterDetailInfo
if self._type == 'totalPvEnergy':
if 'totalPvEnergy' in energy["getPlantMeterDetailInfo"]['plantDetail']:
if energy["getPlantMeterDetailInfo"]['plantDetail']["totalPvEnergy"] is not None:
self._state = (energy["getPlantMeterDetailInfo"]['plantDetail']["totalPvEnergy"])
if self._type == 'totalLoadEnergy':
if 'totalLoadEnergy' in energy["getPlantMeterDetailInfo"]['plantDetail']:
if energy["getPlantMeterDetailInfo"]['plantDetail']["totalLoadEnergy"] is not None:
self._state = (energy["getPlantMeterDetailInfo"]['plantDetail']["totalLoadEnergy"])
if self._type == 'totalBuyEnergy':
if 'totalBuyEnergy' in energy["getPlantMeterDetailInfo"]['plantDetail']:
if energy["getPlantMeterDetailInfo"]['plantDetail']["totalBuyEnergy"] is not None:
self._state = (energy["getPlantMeterDetailInfo"]['plantDetail']["totalBuyEnergy"])
if self._type == 'totalSellEnergy':
if 'totalSellEnergy' in energy["getPlantMeterDetailInfo"]['plantDetail']:
if energy["getPlantMeterDetailInfo"]['plantDetail']["totalSellEnergy"] is not None:
self._state = (energy["getPlantMeterDetailInfo"]['plantDetail']["totalSellEnergy"])
# -Debug- adding sensor
_LOGGER.debug(f"Device: {self._type} State: {self._state}")
@teamMOYA check if by any chance your credentials work on the greenheiss url mentioned in my first comment. you might be lucky and could use the modified version that @miguelzx posted (which I guess is the same I did)
@teamMOYA seems like solar the profit login is at https://inversor.saj-electric.com/cloud?login
my credentials work as well there. pretty sure they just reskin the system with different logos and colors. you probably can use the greenheiss url or this one.
@teamMOYA check if by any chance your credentials work on the greenheiss url mentioned in my first comment. you might be lucky and could use the modified version that @miguelzx posted (which I guess is the same I did)
Hello, yes that's correct, that's what you said, thanks for the help.
That is the file as I have it, in case it is of any help to you.
Since yesterday I spent a hellish afternoon. Restoring backups, and thinking that the problem is that I had updated home assistant
i have a couple of account to test and develop with but all of them seem to work on the original portal so i am guessing it is only the greenheiss that stopped working. If there is anyone willing to share their login details i will see if i can add a option to the configuration yaml to switch to the correct url.
Hello, I also had that problem.
My Greenheiss h1 inverter. I use an old integration 1.3.1 which I already have working.
Right now everything works perfectly for me
replacing the domain in the sensor.py
I leave a copy of my sensor.py, in case it helps you, but remember that I use 1.3.1
Thank you!!
@arnauplan @santiagozky @teamMOYA
Please be aware that the sensor.py above has more changes then only the domainname/url. !!Use at your own risk!!.
@miguelzx could you explain why you have change the H1 part and use beautiful soup to do this? Per my understanding the use of beautiful soup is not permitted by home assistant.
addition: the code used is a very old version of my custom integration. It still uses the old energy StateClass which are not supported by home assistant
@teamMOYA
The same is happening to me. My plant is gone in the "old platform".
Is your plant available in the new portal or do you have to go to the greenheiss portal ?
Is your plant available in the new portal or do you have to go to the greenheiss portal ?
@teamMOYA is your kit branded as SAJ or some other white label (eg. Greenheiss)?
Mine are rebranded as "Solarprofit", but they don't have a web like Saj and Greenheiss. I'll have to wait for the new web access update.
You can still use any of the saj websites with your Solarprofit credentials.
So I guess having a way to configure our endpoint in the addon should suffice, although some websites may have different structure or calls, and that could complicate things.
@djansen1987 I could provide you my credentials if that would help
Has the easiest way been found to be able to see the sensors again? I am new to home assistant.
@Txetxo1979 so far, it seems to find out in which portal you can find your info (greenheiss, solarprofit, etc) and then modify the sensor.py file of the extension to replace the SAJ url with the one that works for you.
@djansen1987 I could provide you my credentials if that would help
That would help a lot. Could you send it to github@djansen.nl promise to handle it with care!
@Txetxo1979 so far, it seems to find out in which portal you can find your info (greenheiss, solarprofit, etc) and then modify the sensor.py file of the extension to replace the SAJ url with the one that works for you.
My URL is https://esaj-home.saj-electric.com/login but I don't know where I can go to change the extension in sensor.py
@Txetxo1979 so far, it seems to find out in which portal you can find your info (greenheiss, solarprofit, etc) and then modify the sensor.py file of the extension to replace the SAJ url with the one that works for you.
My URL is https://esaj-home.saj-electric.com/login but I don't know where I can go to change the extension in sensor.py
This is the new portal, the api is completely changed on that site. Implementing that is almost like starting from scratch.. don't know if i got time for this soon.
@arnauplan @santiagozky @teamMOYA
Please be aware that the sensor.py above has more changes then only the domainname/url. !!Use at your own risk!!.
@miguelzx could you explain why you have change the H1 part and use beautiful soup to do this? Per my understanding the use of beautiful soup is not permitted by home assistant.
addition: the code used is a very old version of my custom integration. It still uses the old energy StateClass which are not supported by home assistant
Hello, my language is not English, so I hope the translator works well.
The version that is theirs, which as I mentioned is 1.3.1, which has worked well for me, until that happened. I use the latest version of home assistan and with my inverter, which is a saj clone, it works perfectly.
What I did was put it in sensor.py, it was replace
https://inversor.saj-electric.com/cloud?login, by https://inversores-style.greenheiss.com/cloud/logou.
and this session = async_create_clientsession(hass,False)
What did the previous colleague say?
@Txetxo1979 so far, it seems to find out in which portal you can find your info (greenheiss, solarprofit, etc) and then modify the sensor.py file of the extension to replace the SAJ url with the one that works for you.
My URL is https://esaj-home.saj-electric.com/login but I don't know where I can go to change the extension in sensor.py
This is the new portal, the api is completely changed on that site. Implementing that is almost like starting from scratch.. don't know if i got time for this soon.
Thank you so much. I just saw that I also have the information at https://inversor.saj-electric.com/cloud?login What should I do then to be able to have the information in home assistant?
@miguelzx
Hola Miguel,
Yo tambien uso 1.3.1. no he actualizado a la nueva version.
el codigo tiene puesto directamente el dominio de saj en varios lugares. Tengo una version que lo actualiza con el dominio de greenheiss.
CUIDADO:tiene algunos cambios mas porque en mi caso que no tengo bateria, reportaba algunos valores en el sensor equivocado (despues de la linea 1136).
Anexo mi fichero tal cual lo tengo. Compararlo con el fichero que tienes en custom_components/saj_esolar/sensor.py
de tu instalación de Home Assistant.
mi sensor.py tal cual lo tengo. it https://gist.github.com/santiagozky/311e830cffd0b54d2fa115162c5ad73c
@Txetxo1979 if you dont mind reverting to 1.3.1. (which is what I still use), you could adjust my gist above to point to your url. just adjust these lines:
BASE_HOST = 'inversores-style.greenheiss.com' BASE_DOMAIN = 'https://'+BASE_HOST BASE_URL = BASE_DOMAIN+'/cloud'
caution: I did additional changes between lines 1138 and 1150 because of a mismatch in my data, probably because I dont have a battery (see https://github.com/djansen1987/SAJeSolar/issues/41), you could skip those changes.
@santiagozky why are you recommending people downgrading and updating a lower version? Latest version can be changed and fixed the same way 😕
@elboletaire I havent recommended anyone to downgrade. Im merely offering the solution I have for myself. Im on 1.3.1 because I had previous changes that I dont want to reapply on newer versions.
Hello, when will the esolar platform be updated so that the sensors work well?
Hi all, sorry for the late response. Currently (still) sick and there for do not have time and energy to work on it. Maybe someone can make a pull request and test it with an SAJ portal user. I am okay to merge it. Otherwise somewhere next weekend I might be able to work on it.
I tried making a change but I've never done anything in python or HASS, so it's taking me quite a long. I might keep giving it a shot, but I cannot make promises on how long it will take me. for those waiting, if you have a minimum programming experience you might be able to adjust it manually replacing all the urls in the sensor.py file.
I opened a PR for this. @djansen1987 feel free to make any adjustment or recommendation you wish. particularly the configuration part. https://github.com/djansen1987/SAJeSolar/pull/67
Good evening. I have an h1 inverter and valid credentials for both the https://esaj-home.saj-electric.com/index portal and for the old https://fop.saj-electric.com/saj.
If I can help, I'd be happy to help
Hi All, thanks to the work of @santiagozky this should now working in the latest version. Please read the release notes to known what to do. Thanks all for the input and patients!
https://github.com/djansen1987/SAJeSolar/releases/tag/v1.5.1
thank you for merging and releasing @djansen1987
I noticed that the integration was not working anymore for me. I accessed the esolar site and noticed that -My plant was gone. no more data -There is a button "new platform" taking me to https://esaj-home.saj-electric.com/index. That new site does shows my data.
Did anyone else experienced this?
A bit more info...
My R5 inversor and esolar module are rebranded with the "Greenheiss" brand (for Spain market). Their version of esolar (which is basically a reskin of SAJ's) still shows my plant https://inversores-style.greenheiss.com/cloud. if anyone's elses integration got killed it might be worth to check that domain.
My guess is they are deprecating the fop site but left Greenheiss users in the old one?
in any case, the integration is probably not working anymore for greenheiss users (and probably others rebranded units ) unless we point to their domain (maybe it can be configured? I'll try to check).