Skip to content

Commit

Permalink
- WebAPI: handing of "missing" fields in JSON response #35
Browse files Browse the repository at this point in the history
- WebAPI Expert setting - specify AnlagenNummer manually (setup)
  • Loading branch information
marq24 committed Oct 12, 2023
1 parent dce731c commit 5fc3771
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 41 deletions.
31 changes: 25 additions & 6 deletions custom_components/senec/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
SYSTEM_MODES,
MODE_WEB,

MASTER_PLANT_NUMBERS,

SYSTYPE_NAME_SENEC,
SYSTYPE_NAME_INVERTER,
SYSTYPE_NAME_WEBAPI,
Expand Down Expand Up @@ -166,12 +168,12 @@ async def _test_connection_inverter(self, host):
)
return False

async def _test_connection_webapi(self, user, pwd):
async def _test_connection_webapi(self, user: str, pwd: str, master_plant:int):
"""Check if we can connect to the Senec WEB."""
self._errors = {}
websession = self.hass.helpers.aiohttp_client.async_get_clientsession()
try:
senec_web_client = MySenecWebPortal(user=user, pwd=pwd, websession=websession)
senec_web_client = MySenecWebPortal(user=user, pwd=pwd, websession=websession, master_plant_number=master_plant)
await senec_web_client.authenticate(doUpdate=False, throw401=True)
if senec_web_client._isAuthenticated:
await senec_web_client.update_context()
Expand Down Expand Up @@ -344,7 +346,6 @@ async def async_step_system(self, user_input=None):
async def async_step_websetup(self, user_input=None):
self._errors = {}
if user_input is not None:
# set some defaults in case we need to return to the form
name = user_input.get(CONF_NAME, DEFAULT_NAME_WEB)
# this is an extremely lousy check!
if "ome V4 " in self._device_type:
Expand All @@ -354,10 +355,20 @@ async def async_step_websetup(self, user_input=None):
user = user_input.get(CONF_USERNAME, DEFAULT_USERNAME)
pwd = user_input.get(CONF_PASSWORD, "")

if self._host_in_configuration_exists(user):
# when the user has multiple masters, the auto-detect does
# not work - so we allow the specification of the AnlagenNummer
master_plant_val = user_input.get(CONF_DEV_MASTER_NUM, "auto")
if master_plant_val == 'auto':
already_exist_ident = user
master_plant_num = -1
else:
already_exist_ident = f"{user}_{master_plant_val}"
master_plant_num = int(master_plant_val)

if self._host_in_configuration_exists(already_exist_ident):
self._errors[CONF_USERNAME] = "already_configured"
else:
if await self._test_connection_webapi(user, pwd):
if await self._test_connection_webapi(user, pwd, master_plant_num):
return self.async_create_entry(title=name, data={CONF_NAME: name,
CONF_HOST: user,
CONF_USERNAME: user,
Expand Down Expand Up @@ -394,7 +405,15 @@ async def async_step_websetup(self, user_input=None):
): str,
vol.Required(
CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "")
): str
): str,
vol.Required(CONF_DEV_MASTER_NUM, default=user_input.get(CONF_DEV_MASTER_NUM, "auto")):
selector.SelectSelector(
selector.SelectSelectorConfig(
options=MASTER_PLANT_NUMBERS,
mode=selector.SelectSelectorMode.DROPDOWN,
translation_key=CONF_DEV_MASTER_NUM,
)
)
}
),
last_step=True,
Expand Down
2 changes: 2 additions & 0 deletions custom_components/senec/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
SYSTYPE_INVERTV3: Final = "systype_invertv3"
SYSTEM_TYPES: Final = [SYSTYPE_SENECV3, SYSTYPE_SENECV4, SYSTYPE_SENECV2, SYSTYPE_WEBAPI, SYSTYPE_INVERTV3]

MASTER_PLANT_NUMBERS: Final = ["auto", "0", "1", "2", "3", "4", "5", "6", "7"]

# the display names of the 3 different implemented backend-types
SYSTYPE_NAME_SENEC = "SENEC Main-Unit"
SYSTYPE_NAME_INVERTER = "SENEC Inverter Module"
Expand Down
98 changes: 66 additions & 32 deletions custom_components/senec/pysenec_ha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2032,22 +2032,34 @@ async def update_now_kW_stats(self):
r_json = await res.json()
self._raw = parse(r_json)
for key in (self._API_KEYS + self._API_KEYS_EXTRA):
if (key != "acculevel"):
value_now = r_json[key]["now"]
entity_now_name = str(key + "_now")
self._power_entities[entity_now_name] = value_now

value_today = r_json[key]["today"]
entity_today_name = str(key + "_today")
self._energy_entities[entity_today_name] = value_today
if key in r_json:
if (key != "acculevel"):
if "now" in r_json[key]:
value_now = r_json[key]["now"]
entity_now_name = str(key + "_now")
self._power_entities[entity_now_name] = value_now
else:
_LOGGER.info(f"No 'now' in json: {r_json} when requesting: {a_url}")

if "today" in r_json[key]:
value_today = r_json[key]["today"]
entity_today_name = str(key + "_today")
self._energy_entities[entity_today_name] = value_today
else:
_LOGGER.info(f"No 'today' in json: {r_json} when requesting: {a_url}")
else:
if "now" in r_json[key]:
value_now = r_json[key]["now"]
entity_now_name = str(key + "_now")
self._battery_entities[entity_now_name] = value_now
else:
_LOGGER.info(f"No 'now' in json: {r_json} when requesting: {a_url}")

# value_today = r_json[key]["today"]
# entity_today_name = str(key + "_today")
# self._battery_entities[entity_today_name]=value_today
else:
value_now = r_json[key]["now"]
entity_now_name = str(key + "_now")
self._battery_entities[entity_now_name] = value_now

# value_today = r_json[key]["today"]
# entity_today_name = str(key + "_today")
# self._battery_entities[entity_today_name]=value_today
_LOGGER.info(f"No '{key}' in json: {r_json} when requesting: {a_url}")

else:
self._isAuthenticated = False
Expand All @@ -2070,9 +2082,12 @@ async def update_full_kWh_stats(self):
res.raise_for_status()
if res.status == 200:
r_json = await res.json()
value = r_json["fullkwh"]
entity_name = str(key + "_total")
self._energy_entities[entity_name] = value
if "fullkwh" in r_json:
value = r_json["fullkwh"]
entity_name = str(key + "_total")
self._energy_entities[entity_name] = value
else:
_LOGGER.info(f"No 'fullkwh' in json: {r_json} when requesting: {api_url}")
else:
self._isAuthenticated = False
await self.update()
Expand All @@ -2088,7 +2103,15 @@ async def update_context(self):
_LOGGER.debug("***** update_context(self) ********")
if self._isAuthenticated:
await self.update_get_customer()
await self.update_get_systems(self._master_plant_number)

# in autodetect-mode the initial self._master_plant_number = -1
if self._master_plant_number == -1:
self._master_plant_number = 0
is_autodetect = True
else:
is_autodetect = False

await self.update_get_systems(a_plant_number=self._master_plant_number, autodetect_mode=is_autodetect)
else:
await self.authenticate(doUpdate=False, throw401=False)

Expand All @@ -2112,29 +2135,40 @@ async def update_get_customer(self):
self._isAuthenticated = False
await self.authenticate(doUpdate=False, throw401=False)

async def update_get_systems(self, a_plant_number: int):
async def update_get_systems(self, a_plant_number: int, autodetect_mode: bool):
_LOGGER.debug("***** update_get_systems(self) ********")

a_url = f"{self._SENEC_API_GET_SYSTEM_URL}" % str(a_plant_number)
async with self.websession.get(a_url) as res:
res.raise_for_status()
if res.status == 200:
r_json = await res.json()

if "master" in r_json and r_json["master"]:
# we are cool that's a master-system... so we store our counter...
if autodetect_mode:
if "master" in r_json and r_json["master"]:
# we are cool that's a master-system... so we store our counter...
self._serial_number = r_json["steuereinheitnummer"]
self._product_name = r_json["produktName"]
if "zoneId" in r_json:
self._zone_id = r_json["zoneId"]
else:
self._zone_id = "UNKNOWN"
self._master_plant_number = a_plant_number
else:
if not hasattr(self, "_serial_number_slave"):
self._serial_number_slave = []
self._product_name_slave = []
self._serial_number_slave.append(r_json["steuereinheitnummer"])
self._product_name_slave.append(r_json["produktName"])
a_plant_number += 1
await self.update_get_systems(a_plant_number, autodetect_mode)
else:
self._serial_number = r_json["steuereinheitnummer"]
self._product_name = r_json["produktName"]
self._zone_id = r_json["zoneId"]
if "zoneId" in r_json:
self._zone_id = r_json["zoneId"]
else:
self._zone_id = "UNKNOWN"
self._master_plant_number = a_plant_number
else:
if not hasattr(self, "_serial_number_slave"):
self._serial_number_slave = []
self._product_name_slave = []
self._serial_number_slave.append(r_json["steuereinheitnummer"])
self._product_name_slave.append(r_json["produktName"])
a_plant_number += 1
await self.update_get_systems(a_plant_number)

else:
self._isAuthenticated = False
Expand Down
3 changes: 2 additions & 1 deletion custom_components/senec/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"host": "[%key:common::config_flow::data::host%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"scan_interval": "[%key:common::config_flow::data::scan_interval%]"
"scan_interval": "[%key:common::config_flow::data::scan_interval%]",
"master_plant_number": "[%key:common::config_flow::data::master_plant_number%]"
}
}
},
Expand Down
16 changes: 15 additions & 1 deletion custom_components/senec/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
"mode_web": "WEB-API: Zugriff über die https://mein-senec.de/ WebAPI",
"mode_local": "LAN: direkter Zugriff über das LAN (IP deines SENEC.Home oder des Wechselrichters)"
}
},
"master_plant_number":{
"options": {
"auto": "Automatische Erkennung (default)",
"0": "Anlagennummer 0",
"1": "Anlagennummer 1",
"2": "Anlagennummer 2",
"3": "Anlagennummer 3",
"4": "Anlagennummer 4",
"5": "Anlagennummer 5",
"6": "Anlagennummer 6",
"7": "Anlagennummer 7"
}
}
},
"config": {
Expand Down Expand Up @@ -46,7 +59,8 @@
"name": "Anzeige Name",
"username": "Dein 'mein-senec.de' Benutzername (E-Mail)",
"password": "Dein 'mein-senec.de' Passwort",
"scan_interval":"Aktualisierungsintervall in Sekunden [> 60 Sekunden (30 Sekunden für SENEC.Home V4)]"
"scan_interval":"Aktualisierungsintervall in Sekunden [> 60 Sekunden (30 Sekunden für SENEC.Home V4)]",
"master_plant_number": "Anlagennummer (Experten Einstellung: Nur bei mehreren Master Instanzen relevant - sonst 'Automatische Erkennung' verwenden)"
}
},
"optional_websetup_required_info": {
Expand Down
16 changes: 15 additions & 1 deletion custom_components/senec/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
"mode_web": "WEB-API: Access via https://mein-senec.de/ web api",
"mode_local": "LAN: Access via local LAN (IP of SENEC.Home or Inverter)"
}
},
"master_plant_number":{
"options": {
"auto": "Autodetect (default)",
"0": "System number 0 (AnlagenNummer)",
"1": "System number 1 (AnlagenNummer)",
"2": "System number 2 (AnlagenNummer)",
"3": "System number 3 (AnlagenNummer)",
"4": "System number 4 (AnlagenNummer)",
"5": "System number 5 (AnlagenNummer)",
"6": "System number 6 (AnlagenNummer)",
"7": "System number 7 (AnlagenNummer)"
}
}
},
"config": {
Expand Down Expand Up @@ -71,7 +84,8 @@
"name": "Display name",
"username": "Your 'mein-senec.de' username",
"password": "Your 'mein-senec.de' password",
"scan_interval":"Polling Interval in seconds [minimum 60 seconds (30 seconds for SENEC.Home V4)]"
"scan_interval":"Polling Interval in seconds [minimum 60 seconds (30 seconds for SENEC.Home V4)]",
"master_plant_number": "System number (expert setting: only when using multiple master instances - otherwise use 'Autodetect')"
}
}
}
Expand Down

0 comments on commit 5fc3771

Please sign in to comment.