diff --git a/custom_components/ecole_directe/__init__.py b/custom_components/ecole_directe/__init__.py index 5cc83a2..c9cd20f 100644 --- a/custom_components/ecole_directe/__init__.py +++ b/custom_components/ecole_directe/__init__.py @@ -1,4 +1,5 @@ """Ecole Directe integration.""" + from __future__ import annotations from homeassistant.config_entries import ConfigEntry @@ -11,17 +12,14 @@ from .coordinator import EDDataUpdateCoordinator -from .const import ( - DEFAULT_REFRESH_INTERVAL, - DOMAIN, - PLATFORMS -) +from .const import DEFAULT_REFRESH_INTERVAL, DOMAIN, PLATFORMS _LOGGER = logging.getLogger(__name__) + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Ecole Directe from a config entry.""" - _LOGGER.debug(f"async_setup_entry") + _LOGGER.debug("async_setup_entry") hass.data.setdefault(DOMAIN, {}) coordinator = EDDataUpdateCoordinator(hass, entry) @@ -41,6 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): @@ -48,8 +47,11 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok + async def update_listener(hass: HomeAssistant, entry: ConfigEntry): """Handle options update.""" - hass.data[DOMAIN][entry.entry_id]['coordinator'].update_interval = timedelta(minutes=entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL)) + hass.data[DOMAIN][entry.entry_id]["coordinator"].update_interval = timedelta( + minutes=entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL) + ) - return True \ No newline at end of file + return True diff --git a/custom_components/ecole_directe/config_flow.py b/custom_components/ecole_directe/config_flow.py index f1f3abe..befea66 100644 --- a/custom_components/ecole_directe/config_flow.py +++ b/custom_components/ecole_directe/config_flow.py @@ -1,4 +1,5 @@ """Config flow for Ecole Directe integration.""" + from __future__ import annotations import logging @@ -6,14 +7,14 @@ import voluptuous as vol -from datetime import time - from homeassistant import config_entries from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError from homeassistant.core import callback -from .ecoleDirecte_helper import * +from .ecoleDirecte_helper import ( + get_ecoledirecte_session, +) from .const import ( DOMAIN, @@ -26,10 +27,10 @@ { vol.Required("username"): str, vol.Required("password"): str, - vol.Required("account_type"): vol.In({'famille': 'Famille', 'eleve': 'Elève'}) } ) + @config_entries.HANDLERS.register(DOMAIN) class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Ecole Directe.""" @@ -46,19 +47,23 @@ async def async_step_user(self, user_input: dict | None = None) -> FlowResult: errors: dict[str, str] = {} if user_input is not None: try: - _LOGGER.debug("ED - User Input: %s", user_input) self._user_inputs.update(user_input) - session = await self.hass.async_add_executor_job(get_ecoledirecte_session, self._user_inputs) + session = await self.hass.async_add_executor_job( + get_ecoledirecte_session, self._user_inputs + ) if session is None: raise InvalidAuth - _LOGGER.debug(f"ED - User Inputs UP: {self._user_inputs} - identifiant: [{session.identifiant}]") - return self.async_create_entry(title=session.identifiant, data=self._user_inputs) + return self.async_create_entry( + title=session.identifiant, data=self._user_inputs + ) except InvalidAuth: errors["base"] = "invalid_auth" - return self.async_show_form(step_id="user", data_schema=STEP_USER_DATA_SCHEMA_UP, errors=errors) + return self.async_show_form( + step_id="user", data_schema=STEP_USER_DATA_SCHEMA_UP, errors=errors + ) @staticmethod @callback @@ -68,6 +73,7 @@ def async_get_options_flow( """Create the options flow.""" return OptionsFlowHandler(config_entry) + class CannotConnect(HomeAssistantError): """Error to indicate we cannot connect.""" @@ -75,6 +81,7 @@ class CannotConnect(HomeAssistantError): class InvalidAuth(HomeAssistantError): """Error to indicate there is invalid auth.""" + class OptionsFlowHandler(config_entries.OptionsFlow): def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize options flow.""" @@ -91,7 +98,12 @@ async def async_step_init( step_id="init", data_schema=vol.Schema( { - vol.Optional("refresh_interval", default=self.config_entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL)): int, + vol.Optional( + "refresh_interval", + default=self.config_entry.options.get( + "refresh_interval", DEFAULT_REFRESH_INTERVAL + ), + ): int, } ), - ) \ No newline at end of file + ) diff --git a/custom_components/ecole_directe/const.py b/custom_components/ecole_directe/const.py index ab32a16..fbf1c2a 100644 --- a/custom_components/ecole_directe/const.py +++ b/custom_components/ecole_directe/const.py @@ -6,4 +6,6 @@ PLATFORMS = [Platform.SENSOR] # default values for options -DEFAULT_REFRESH_INTERVAL=30 +DEFAULT_REFRESH_INTERVAL = 30 +EVALUATIONS_TO_DISPLAY = 15 +HOMEWORK_DESC_MAX_LENGTH = 125 diff --git a/custom_components/ecole_directe/coordinator.py b/custom_components/ecole_directe/coordinator.py index d5a505b..2bfd4a7 100644 --- a/custom_components/ecole_directe/coordinator.py +++ b/custom_components/ecole_directe/coordinator.py @@ -1,37 +1,40 @@ """Data update coordinator for the Ecole Directe integration.""" + from __future__ import annotations +import datetime +import logging from datetime import timedelta from typing import Any -import logging -from .ecoleDirecte_helper import * - from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import TimestampDataUpdateCoordinator +from .ecoleDirecte_helper import get_ecoledirecte_session, getHomework, getNotes + from .const import ( DEFAULT_REFRESH_INTERVAL, ) _LOGGER = logging.getLogger(__name__) + class EDDataUpdateCoordinator(TimestampDataUpdateCoordinator): """Data update coordinator for the Ecole Directe integration.""" config_entry: ConfigEntry - def __init__( - self, hass: HomeAssistant, entry: ConfigEntry - ) -> None: + def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: """Initialize the coordinator.""" super().__init__( hass=hass, logger=_LOGGER, name=entry.title, - update_interval=timedelta(minutes=entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL)), + update_interval=timedelta( + minutes=entry.options.get("refresh_interval", DEFAULT_REFRESH_INTERVAL) + ), ) self.config_entry = entry @@ -39,40 +42,53 @@ async def _async_update_data(self) -> dict[Platform, dict[str, Any]]: """Get the latest data from Ecole Directe and updates the state.""" data = self.config_entry.data - self.data = { - "account_type": data['account_type'], - } session = await self.hass.async_add_executor_job(get_ecoledirecte_session, data) if session is None: - _LOGGER.error('Unable to init ecole directe client') + _LOGGER.error("Unable to init ecole directe client") return None - self.data['session'] = session + self.data = {} + self.data["session"] = session + + currentYear = datetime.datetime.now().year + if (currentYear % 2) == 0: + yearData = f"{str(currentYear-1)}-{str(currentYear)}" + else: + yearData = f"{str(currentYear)}-{str(currentYear + 1)}" - if (data['account_type'] == 'famille'): - try: - self.data['messages'] = await self.hass.async_add_executor_job(getMessages, session, None, None) - except Exception as ex: - self.data['messages'] = None - _LOGGER.warning("Error getting messages for family from ecole directe: %s", ex) + # if (session.typeCompte == '1'): # famille + # if "MESSAGERIE" in session.modules: + # try: + # self.data['messages'] = await self.hass.async_add_executor_job(getMessages, session, None, yearData) + # except Exception as ex: + # self.data['messages'] = None + # _LOGGER.warning("Error getting messages for family from ecole directe: %s", ex) for eleve in session.eleves: if "CAHIER_DE_TEXTES" in eleve.modules: try: - self.data['homework'+ eleve.eleve_id] = await self.hass.async_add_executor_job(getHomework, session, eleve) + self.data[ + "homework" + eleve.get_fullnameLower() + ] = await self.hass.async_add_executor_job( + getHomework, session, eleve + ) except Exception as ex: _LOGGER.warning("Error getting homework from ecole directe: %s", ex) if "NOTES" in eleve.modules: try: - self.data['notes'+ eleve.eleve_id] = await self.hass.async_add_executor_job(getNotes, session, eleve) - except Exception as ex: - _LOGGER.warning("Error getting notes from ecole directe: %s", ex) - if "MESSAGERIE" in eleve.modules: - try: - self.data['notes'+ eleve.eleve_id] = await self.hass.async_add_executor_job(getMessages, session, eleve, None) + self.data[ + "notes" + eleve.get_fullnameLower() + ] = await self.hass.async_add_executor_job( + getNotes, session, eleve, yearData + ) except Exception as ex: _LOGGER.warning("Error getting notes from ecole directe: %s", ex) + # if "MESSAGERIE" in eleve.modules: + # try: + # self.data['messages'+ eleve.eleve_id] = await self.hass.async_add_executor_job(getMessages, session, eleve, yearData) + # except Exception as ex: + # _LOGGER.warning("Error getting notes from ecole directe: %s", ex) return self.data diff --git a/custom_components/ecole_directe/ecoleDirecte_formatter.py b/custom_components/ecole_directe/ecoleDirecte_formatter.py new file mode 100644 index 0000000..eace282 --- /dev/null +++ b/custom_components/ecole_directe/ecoleDirecte_formatter.py @@ -0,0 +1,13 @@ +"""Data Formatter for the Ecole Directe integration.""" + +import logging + +_LOGGER = logging.getLogger(__name__) + + +def format_homework(homework) -> dict: + return None # TODO + + +def format_note(note) -> dict: + return None # TODO diff --git a/custom_components/ecole_directe/ecoleDirecte_helper.py b/custom_components/ecole_directe/ecoleDirecte_helper.py index 6d1b4e8..4c86c1a 100644 --- a/custom_components/ecole_directe/ecoleDirecte_helper.py +++ b/custom_components/ecole_directe/ecoleDirecte_helper.py @@ -1,34 +1,30 @@ import re import requests -import base64 import logging +import urllib _LOGGER = logging.getLogger(__name__) -APIURL="https://api.ecoledirecte.com/v3" -APIVERSION="4.53.0" +APIURL = "https://api.ecoledirecte.com/v3" +APIVERSION = "4.53.0" -def encodeString(string): - return string.replace("%", "%25").replace("&", "%26").replace("+", "%2B").replace("+", "%2B").replace("\\", "\\\\\\").replace("\\\\", "\\\\\\\\") -def encodeBody(dictionnary, isRecursive = False): +def encodeBody(dictionnary, isRecursive=False): body = "" for key in dictionnary: if isRecursive: - body += "\"" + key + "\":" + body += '"' + key + '":' else: body += key + "=" - if type(dictionnary[key]) is dict: + if isinstance(dictionnary[key], dict): body += "{" + encodeBody(dictionnary[key], True) + "}" else: - body += "\"" + str(dictionnary[key]) + "\"" + body += '"' + urllib.parse.quote(str(dictionnary[key]), safe="") + '"' body += "," return body[:-1] -def decodeB64(string): - return base64.b64decode(string) def getResponse(session, url, data): if session is not None and isLogin(session): @@ -36,20 +32,28 @@ def getResponse(session, url, data): else: token = None - response = requests.post(url, data = data, headers = getHeaders(token)) + _LOGGER.debug("URL: [%s] - Data: [%s]", url, data) + response = requests.post(url, data=data, headers=getHeaders(token)) - if 'application/json' in response.headers.get('Content-Type', ''): + if "application/json" in response.headers.get("Content-Type", ""): respJson = response.json() - if respJson['code'] != 200: - raise RequestError('Error with URL:[{}] - Code {}: {}'.format(url, respJson['code'], respJson['message'])) + if respJson["code"] != 200: + raise RequestError( + "Error with URL:[{}] - Code {}: {}".format( + url, respJson["code"], respJson["message"] + ) + ) + _LOGGER.debug("%s", respJson) return respJson - raise RequestError('Error with URL:[{}]: {}'.format(url, response.content)) + raise RequestError("Error with URL:[{}]: {}".format(url, response.content)) + class RequestError(Exception): def __init__(self, message): super(RequestError, self).__init__(message) + class ED_Session: def __init__(self, data): self.token = data["token"] @@ -57,22 +61,57 @@ def __init__(self, data): self.identifiant = data["data"]["accounts"][0]["identifiant"] self.idLogin = data["data"]["accounts"][0]["idLogin"] self.typeCompte = data["data"]["accounts"][0]["typeCompte"] - self.eleves = [] - if data["data"]["accounts"][0]["profile"]["eleves"] is not None: - for eleve in data["data"]["accounts"][0]["profile"]["eleves"]: - self.eleves.append(ED_Eleve(eleve)) - -class ED_Eleve: - def __init__(self, data): - self.classe_id = data["classe"]["id"] - self.classe_name = data["classe"]["libelle"] - self.eleve_id = data["id"] - self.eleve_lastname = data["nom"] - self.eleve_firstname = data["prenom"] self.modules = [] - for module in data["modules"]: + for module in data["data"]["accounts"][0]["modules"]: if module["enable"]: self.modules.append(module["code"]) + self.eleves = [] + if self.typeCompte == "E": + self.eleves.append( + ED_Eleve( + None, + self.id, + data["data"]["accounts"][0]["prenom"], + data["data"]["accounts"][0]["nom"], + data["data"]["accounts"][0]["profile"]["classe"]["id"], + data["data"]["accounts"][0]["profile"]["classe"]["libelle"], + self.modules, + ) + ) + else: + if "eleves" in data["data"]["accounts"][0]["profile"]: + for eleve in data["data"]["accounts"][0]["profile"]["eleves"]: + self.eleves.append(ED_Eleve(eleve)) + + +class ED_Eleve: + def __init__( + self, + data, + id=None, + firstName=None, + lastName=None, + classe_id=None, + classe_name=None, + modules=None, + ): + if data is None: + self.classe_id = classe_id + self.classe_name = classe_name + self.eleve_id = id + self.eleve_lastname = lastName + self.eleve_firstname = firstName + self.modules = modules + else: + self.classe_id = data["classe"]["id"] + self.classe_name = data["classe"]["libelle"] + self.eleve_id = data["id"] + self.eleve_lastname = data["nom"] + self.eleve_firstname = data["prenom"] + self.modules = [] + for module in data["modules"]: + if module["enable"]: + self.modules.append(module["code"]) def get_fullnameLower(self) -> str | None: return f"{re.sub("[^A-Za-z]", "_", self.eleve_firstname.lower())}_{re.sub("[^A-Za-z]", "_", self.eleve_lastname.lower())}" @@ -80,48 +119,72 @@ def get_fullnameLower(self) -> str | None: def get_fullname(self) -> str | None: return f"{self.eleve_firstname} {self.eleve_lastname}" + def get_ecoledirecte_session(data) -> ED_Session | None: try: - _LOGGER.debug(f"Try connection for username: {data['username']} and password: {data['password']}") - - login = getResponse(None, f"{APIURL}/login.awp?v={APIVERSION}", encodeBody({ - "data": { - "identifiant": data['username'], - "motdepasse": data['password'] - } - })) - - _LOGGER.debug(f"login: {login}") - _LOGGER.info(f"Connection OK - identifiant: [{login["data"]["accounts"][0]["identifiant"]}]") - return ED_Session(login); - + _LOGGER.debug( + f"Try connection for username: {data['username']} and password: {data['password']}" + ) + + login = getResponse( + None, + f"{APIURL}/login.awp?v={APIVERSION}", + encodeBody( + { + "data": { + "identifiant": data["username"], + "motdepasse": data["password"], + } + } + ), + ) + + _LOGGER.info( + f"Connection OK - identifiant: [{login["data"]["accounts"][0]["identifiant"]}]" + ) + return ED_Session(login) except Exception as err: _LOGGER.critical(err) return None -def getMessages(session, eleve, year): - if(eleve == None): - return getResponse(session, - f"{APIURL}/familles/{session.id}/messages.awp?force=false&typeRecuperation=received&idClasseur=0&orderBy=date&order=desc&query=&onlyRead=&page=0&itemsPerPage=100&getAll=0&verbe=get&v={APIVERSION}", - encodeBody({"data": {"anneeMessages": year}})) - return getResponse(session, - f"{APIURL}/eleves/{eleve.eleve_id}/messages.awp?force=false&typeRecuperation=received&idClasseur=0&orderBy=date&order=desc&query=&onlyRead=&page=0&itemsPerPage=100&getAll=0&verbe=get&v={APIVERSION}", - encodeBody({"data": {"anneeMessages": year}})) -def getHomework(session, eleve, date): - return getResponse(session, - f"{APIURL}/eleves/{eleve.eleve_id}/cahierdetexte/{date}.awp?verbe=get&v={APIVERSION}", - "data={}") +def getMessages(session, eleve, anneeScolaire): + if eleve is None: + return getResponse( + session, + f"{APIURL}/familles/{session.id}/messages.awp?force=false&typeRecuperation=received&idClasseur=0&orderBy=date&order=desc&query=&onlyRead=&page=0&itemsPerPage=100&getAll=0&verbe=get&v={APIVERSION}", + encodeBody({"data": {"anneeMessages": anneeScolaire}}), + ) + return getResponse( + session, + f"{APIURL}/eleves/{eleve.eleve_id}/messages.awp?force=false&typeRecuperation=received&idClasseur=0&orderBy=date&order=desc&query=&onlyRead=&page=0&itemsPerPage=100&getAll=0&verbe=get&v={APIVERSION}", + encodeBody({"data": {"anneeMessages": anneeScolaire}}), + ) + + +def getHomeworkByDate(session, eleve, date): + return getResponse( + session, + f"{APIURL}/eleves/{eleve.eleve_id}/cahierdetexte/{date}.awp?verbe=get&v={APIVERSION}", + "data={}", + ) + def getHomework(session, eleve): - return getResponse(session, - f"{APIURL}/eleves/{eleve.eleve_id}/cahierdetexte.awp?verbe=get&v={APIVERSION}", - "data={}") + return getResponse( + session, + f"{APIURL}/eleves/{eleve.eleve_id}/cahierdetexte.awp?verbe=get&v={APIVERSION}", + "data={}", + ) + + +def getNotes(session, eleve, anneeScolaire): + return getResponse( + session, + f"{APIURL}/eleves/{eleve.eleve_id}/notes.awp?verbe=get&v={APIVERSION}", + encodeBody({"data": {"anneeScolaire": anneeScolaire}}), + ) -def getNotes(session, eleve): - return getResponse(session, - f"{APIURL}/eleves/{eleve.eleve_id}/notes.awp?verbe=get&v={APIVERSION}", - "data='data={\"anneeScolaire\": \"\"}'") def getHeaders(token): headers = { @@ -138,16 +201,16 @@ def getHeaders(token): "Sec-fetch-mode": "cors", "Sec-fetch-site": "same-site", "Sec-GPC": "1", - "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0" + "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0", } - if token != None: + if token is not None: headers["X-Token"] = token return headers + def isLogin(session): - if session.token != None and session.id != None: + if session.token is not None and session.id is not None: return True return False - diff --git a/custom_components/ecole_directe/sensor.py b/custom_components/ecole_directe/sensor.py index d839e2f..e8d5b6a 100644 --- a/custom_components/ecole_directe/sensor.py +++ b/custom_components/ecole_directe/sensor.py @@ -10,25 +10,27 @@ CoordinatorEntity, ) -from .ecoleDirecte_helper import * +from .ecoleDirecte_formatter import format_note, format_homework +from .ecoleDirecte_helper import ED_Eleve from .coordinator import EDDataUpdateCoordinator from .const import ( DOMAIN, + EVALUATIONS_TO_DISPLAY, ) + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback + async_add_entities: AddEntitiesCallback, ) -> None: - coordinator: EDDataUpdateCoordinator = hass.data[DOMAIN][ - config_entry.entry_id - ]["coordinator"] + coordinator: EDDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id][ + "coordinator" + ] sensors = [] - eleves = coordinator.data["session"].eleves - for eleve in eleves: + for eleve in coordinator.data["session"].eleves: sensors.append(EDChildSensor(coordinator, eleve)) if "CAHIER_DE_TEXTES" in eleve.modules: sensors.append(EDHomeworkSensor(coordinator, eleve)) @@ -37,27 +39,36 @@ async def async_setup_entry( async_add_entities(sensors, False) + class EDGenericSensor(CoordinatorEntity, SensorEntity): """Representation of a ED sensor.""" - def __init__(self, coordinator, coordinator_key: str, name: str, eleve: ED_Eleve = None, state: str = None, device_class: str = None) -> None: + def __init__( + self, + coordinator, + name: str, + eleve: ED_Eleve = None, + state: str = None, + device_class: str = None, + ) -> None: """Initialize the ED sensor.""" super().__init__(coordinator) - self._coordinator_key = coordinator_key - self._name = f"{eleve.get_fullnameLower()}_{name}" if eleve != None else name + + identifiant = self.coordinator.data["session"].identifiant + + self._name = name self._state = state - self._attr_unique_id = f"ed-{self.coordinator.data["session"].identifiant}-{self._name}" - id = eleve.get_fullname() if eleve != None else self.coordinator.data["session"].identifiant + self._child_info = eleve + self._attr_unique_id = f"ed_{identifiant}_{self._name}" self._attr_device_info = DeviceInfo( - name = f"ED - {id}", - entry_type = DeviceEntryType.SERVICE, - identifiers = { - (DOMAIN, f"ED - {id}") - }, - manufacturer = "EcoleDirecte", - model = id, + name=identifiant, + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, f"ED - {identifiant}")}, + manufacturer="Ecole Directe", + model=str(f"ED - {identifiant}"), ) - if (device_class is not None): + + if device_class is not None: self._attr_device_class = device_class @property @@ -68,48 +79,40 @@ def name(self): @property def native_value(self): """Return the state of the sensor.""" - if self.coordinator.data[self._coordinator_key] is None: - return 'unavailable' - elif self._state == 'len': - return len(self.coordinator.data[self._coordinator_key]) + if self.coordinator.data[self._name] is None: + return "unavailable" + elif self._state == "len": + return len(self.coordinator.data[self._name]) elif self._state is not None: return self._state - return self.coordinator.data[self._coordinator_key] + return self.coordinator.data[self._name] @property def extra_state_attributes(self): """Return the state attributes.""" - return { - 'updated_at': self.coordinator.last_update_success_time - } + return {"updated_at": self.coordinator.last_update_success_time} @property def available(self) -> bool: """Return if entity is available.""" - return self.coordinator.last_update_success and self._coordinator_key in self.coordinator.data + return ( + self.coordinator.last_update_success + and self.coordinator.data[self._name] is not None + ) + -class EDChildSensor(CoordinatorEntity, SensorEntity): +class EDChildSensor(EDGenericSensor): """Representation of a ED child sensor.""" - def __init__(self, coordinator, eleve: ED_Eleve) -> None: + def __init__(self, coordinator: EDDataUpdateCoordinator, eleve: ED_Eleve) -> None: """Initialize the ED sensor.""" - super().__init__(coordinator) - self._child_info = eleve - self._attr_unique_id = f"ed-{eleve.get_fullnameLower()}-{eleve.eleve_id}]" - self._attr_device_info = DeviceInfo( - name=f"ED - {eleve.get_fullname()}", - entry_type=DeviceEntryType.SERVICE, - identifiers={ - (DOMAIN, f"ED - {eleve.get_fullname()}") - }, - manufacturer="Ecole Directe", - model=eleve.get_fullname(), - ) + super().__init__(coordinator, eleve.get_fullname(), eleve, "len") + self._attr_unique_id = f"ed_{eleve.get_fullnameLower()}_{eleve.eleve_id}]" @property def name(self): """Return the name of the sensor.""" - return f"{DOMAIN}_{self._child_info.get_fullname()}" + return self._child_info.get_fullname() @property def native_value(self): @@ -122,16 +125,46 @@ def extra_state_attributes(self): return { "full_name": self._child_info.get_fullname(), "class_name": self._child_info.classe_name, - "updated_at": self.coordinator.last_update_success_time + "updated_at": self.coordinator.last_update_success_time, } + @property + def available(self) -> bool: + """Return if entity is available.""" + return self.coordinator.last_update_success + + class EDHomeworkSensor(EDGenericSensor): """Representation of a ED sensor.""" def __init__(self, coordinator: EDDataUpdateCoordinator, eleve: ED_Eleve) -> None: """Initialize the ED sensor.""" - super().__init__(coordinator, 'homework' + str(eleve.eleve_id), 'homework' + str(eleve.eleve_id), eleve, 'len') - self._suffix = eleve.eleve_id + super().__init__( + coordinator, "homework" + eleve.get_fullnameLower(), eleve, "len" + ) + + @property + def extra_state_attributes(self): + """Return the state attributes.""" + attributes = [] + todo_counter = None + if ( + self.coordinator.data[f"homework{self._child_info.get_fullnameLower()}"] + is not None + ): + todo_counter = 0 + for homework in self.coordinator.data[ + f"homework{self._child_info.get_fullnameLower()}" + ]: + attributes.append(format_homework(homework)) + if homework.done is False: + todo_counter += 1 + + return { + "updated_at": self.coordinator.last_update_success_time, + "homework": attributes, + "todo_counter": todo_counter, + } class EDNotesSensor(EDGenericSensor): @@ -139,5 +172,26 @@ class EDNotesSensor(EDGenericSensor): def __init__(self, coordinator: EDDataUpdateCoordinator, eleve: ED_Eleve) -> None: """Initialize the ED sensor.""" - super().__init__(coordinator, 'notes' + str(eleve.eleve_id), 'notes' + str(eleve.eleve_id), eleve, 'len') - self._suffix = eleve.eleve_id + super().__init__(coordinator, "notes" + eleve.get_fullnameLower(), eleve, "len") + + @property + def extra_state_attributes(self): + """Return the state attributes.""" + attributes = [] + index_note = 0 + if ( + self.coordinator.data["notes" + self._child_info.get_fullnameLower()] + is not None + ): + for note in self.coordinator.data[ + "notes" + self._child_info.get_fullnameLower() + ]: + index_note += 1 + if index_note == EVALUATIONS_TO_DISPLAY: + break + attributes.append(format_note(note)) + + return { + "updated_at": self.coordinator.last_update_success_time, + "evaluations": attributes, + } diff --git a/custom_components/ecole_directe/strings.json b/custom_components/ecole_directe/strings.json index d914bff..99186bf 100644 --- a/custom_components/ecole_directe/strings.json +++ b/custom_components/ecole_directe/strings.json @@ -8,13 +8,11 @@ "description": "Donnez vos informations de connexion à Ecole Directe", "data": { "username": "[%key:common::config_flow::data::username%]", - "password": "[%key:common::config_flow::data::password%]", - "account_type": "[%key:common::config_flow::data::account_type%]" + "password": "[%key:common::config_flow::data::password%]" }, "data_description": { "username": "user name", - "password": "Password", - "account_type": "Account type" + "password": "Password" } } }, diff --git a/custom_components/ecole_directe/translations/en.json b/custom_components/ecole_directe/translations/en.json index 4dd1d2a..979fe2a 100644 --- a/custom_components/ecole_directe/translations/en.json +++ b/custom_components/ecole_directe/translations/en.json @@ -14,8 +14,7 @@ "user": { "data": { "password": "Password", - "username": "Username", - "account_type": "Account type" + "username": "Username" } } } diff --git a/custom_components/ecole_directe/translations/fr.json b/custom_components/ecole_directe/translations/fr.json index 727ee92..b816678 100644 --- a/custom_components/ecole_directe/translations/fr.json +++ b/custom_components/ecole_directe/translations/fr.json @@ -14,8 +14,7 @@ "user": { "data": { "password": "Mot de passe", - "username": "Identifiant", - "account_type": "Type de compte" + "username": "Identifiant" } } }