From b120ab03ed05bda86851f2986117a49e4f261f4f Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sat, 23 Dec 2023 17:12:56 +0000 Subject: [PATCH 1/8] README updates --- README.md | 6 ++++++ custom_components/little_monkey/manifest.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2765213..052e677 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +[![GitHub Release][releases-shield]][releases] +[![CodeQL](https://github.com/jmcruvellier/little_monkey/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/github-code-scanning/codeql) +[![HACS](https://github.com/jmcruvellier/little_monkey/actions/workflows/hacs.yaml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/hacs.yaml) +[![hassfest](https://github.com/jmcruvellier/little_monkey/actions/workflows/hassfest.yaml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/hassfest.yaml) +[![Validate](https://github.com/jmcruvellier/little_monkey/actions/workflows/validate.yml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/validate.yml) + ![](/custom_components/little_monkey/res/logo_small.png) # Little Monkey / Petit Singe diff --git a/custom_components/little_monkey/manifest.json b/custom_components/little_monkey/manifest.json index 72f6a3d..af2c680 100644 --- a/custom_components/little_monkey/manifest.json +++ b/custom_components/little_monkey/manifest.json @@ -5,7 +5,7 @@ "@jmcruvellier" ], "config_flow": true, - "documentation": "https://github.com/jmcruvellier/little_monkey/blob/v0.1.1/README.md", + "documentation": "https://github.com/jmcruvellier/little_monkey/blob/v1.0.1/README.md", "integration_type": "device", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/jmcruvellier/little_monkey/issues", From 3866e45130c564d034a6e487b6559122af3caa89 Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sat, 23 Dec 2023 17:16:35 +0000 Subject: [PATCH 2/8] README updates --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 052e677..69c0d64 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![GitHub Release][releases-shield]][releases] [![CodeQL](https://github.com/jmcruvellier/little_monkey/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/github-code-scanning/codeql) [![HACS](https://github.com/jmcruvellier/little_monkey/actions/workflows/hacs.yaml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/hacs.yaml) [![hassfest](https://github.com/jmcruvellier/little_monkey/actions/workflows/hassfest.yaml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/hassfest.yaml) From e92269f9ffb0ade86141b3d0f47f823b608874dd Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sun, 4 Feb 2024 08:16:27 +0000 Subject: [PATCH 3/8] #58 + #71 + Bug fixes + Performance + Readme --- README.md | 39 +- custom_components/little_monkey/__init__.py | 23 +- custom_components/little_monkey/api.py | 346 ++++++++++++------ .../little_monkey/config_flow.py | 15 +- custom_components/little_monkey/const.py | 3 +- .../little_monkey/coordinator.py | 4 + .../little_monkey_translations/en.json | 6 +- .../little_monkey_translations/fr.json | 6 +- custom_components/little_monkey/manifest.json | 4 +- custom_components/little_monkey/sensor.py | 29 +- .../little_monkey/translations/en.json | 2 + .../little_monkey/translations/fr.json | 2 + 12 files changed, 342 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index 69c0d64..f7ff337 100644 --- a/README.md +++ b/README.md @@ -10,24 +10,27 @@ Cette intégration vous permet de récupérer les informations collectées par v Elle intègre dans Home Assistant les capteurs suivants: -* Consommation Temps Réel (Puissance en W) -* Consommation Réseau (Energie en kWh) -* Si vous avez un contrat d'énergie HC/HP - - Consommation HC Réseau (Energie en kWh) - - Consommation HP Réseau (Energie en kWh) - - Si c'est un contrat Tempo: - - Consommation HC Bleu Réseau (Energie en kWh) - - Consommation HP Bleu Réseau (Energie en kWh) - - Consommation HC Blanc Réseau (Energie en kWh) - - Consommation HP Blanc Réseau (Energie en kWh) - - Consommation HC Rouge Réseau (Energie en kWh) - - Consommation HP Rouge Réseau (Energie en kWh) -* Si vous êtes producteur d'énergie grâce à des panneaux photovoltaïques et possesseur d'un capteur ecojoko ancienne génération: - - Surplus de Production (Energie en kWh) -* Température Intérieure (en °C) -* Température Extérieure (en °C) -* Humidité Intérieure (en %) -* Humidité Extérieure (en %) +| Version | Capteur | Type | Unité | Disponibilité | Commentaire | +| ------- | ------- | ---- | ----- | ------------- | ----------- | +| 1.0.0 | Consommation Temps Réel | Puissance | W | Permanent | | +| 1.0.0 | Consommation Réseau | Energie | kWh | Permanent | | +| 1.0.0 | Consommation HC Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie incluant les HC/HP | +| 1.0.0 | Consommation HP Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie incluant les HC/HP | +| 1.0.0 | Consommation HC Bleu Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HP Bleu Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HC Blanc Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HP Blanc Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HC Rouge Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HP Rouge Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Surplus de Production | Energie | kWh | Optionnel | Si vous êtes producteur d'énergie grâce à des panneaux photovoltaïques et possesseur d'un capteur ecojoko ancienne génération | +| 1.0.0 | Température Intérieure | Température | °C | Optionnel | | +| 1.0.0 | Température Extérieure | Température | °C | Optionnel | | +| 1.0.0 | Humidité Intérieure | Humidité | % | Optionnel | | +| 1.0.0 | Humidité Extérieure | Humidité | % | Optionnel | | +| 1.1.0 | Consommation Dernière Mesure | Puissance | W | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | +| 1.1.0 | Consommation Réseau Dernière Mesure | Energie | kWh | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | +| 1.1.0 | Consommation HC Réseau Dernière Mesure | Energie | kWh | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | +| 1.1.0 | Consommation HP Réseau Dernière Mesure | Energie | kWh | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | > [!IMPORTANT] > Si vous êtes un utilisateur régulier de l'application ecojoko©️, vous n'êtes pas sans savoir que le petit singe glisse souvent sur sa peau de banane. **Cette __intégration non-officielle__ dépend des APIs d'ecojoko©️ et n'est donc pas responsable en cas d'indisponibilité de vos donnés.** diff --git a/custom_components/little_monkey/__init__.py b/custom_components/little_monkey/__init__.py index 281a78e..5f8a09e 100644 --- a/custom_components/little_monkey/__init__.py +++ b/custom_components/little_monkey/__init__.py @@ -14,6 +14,7 @@ from .const import ( DOMAIN, PLATFORMS, + CONF_USE_LAST_MEASURE_FEATURE, CONF_USE_HCHP_FEATURE, CONF_USE_TEMPO_FEATURE, CONF_USE_TEMPHUM_FEATURE, @@ -21,6 +22,15 @@ ) from .coordinator import LittleMonkeyDataUpdateCoordinator +def get_boolean(array, index): + """Read the value with a default of False if the key is not found""" + return array.get(index, False) + +def get_string(array, index): + """Read the value with a default of empty string if the key is not found""" + return array.get(index, "") + + # https://developers.home-assistant.io/docs/config_entries_index/#setting-up-an-entry async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up this integration using UI.""" @@ -29,12 +39,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass=hass, entry=entry, client=LittleMonkeyApiClient( - username=entry.data[CONF_USERNAME], - password=entry.data[CONF_PASSWORD], - use_hchp=entry.data[CONF_USE_HCHP_FEATURE], - use_tempo=entry.data[CONF_USE_TEMPO_FEATURE], - use_temphum=entry.data[CONF_USE_TEMPHUM_FEATURE], - use_prod=entry.data[CONF_USE_PROD_FEATURE], + username=get_string(entry.data, CONF_USERNAME), + password=get_string(entry.data, CONF_PASSWORD), + use_last_measure=get_boolean(entry.data, CONF_USE_LAST_MEASURE_FEATURE), + use_hchp=get_boolean(entry.data, CONF_USE_HCHP_FEATURE), + use_tempo=get_boolean(entry.data, CONF_USE_TEMPO_FEATURE), + use_temphum=get_boolean(entry.data, CONF_USE_TEMPHUM_FEATURE), + use_prod=get_boolean(entry.data, CONF_USE_PROD_FEATURE), session=async_get_clientsession(hass), ), ) diff --git a/custom_components/little_monkey/api.py b/custom_components/little_monkey/api.py index 73116ea..d0d90b9 100644 --- a/custom_components/little_monkey/api.py +++ b/custom_components/little_monkey/api.py @@ -1,7 +1,7 @@ """API Client for little_monkey.""" from __future__ import annotations -# import traceback +#import traceback import asyncio import datetime @@ -59,6 +59,7 @@ def __init__( self, username: str, password: str, + use_last_measure: bool, use_hchp: bool, use_tempo: bool, use_temphum: bool, @@ -68,6 +69,7 @@ def __init__( """Initialize.""" self._username = username self._password = password + self._use_last_measure = use_last_measure self._use_hchp = use_hchp self._use_tempo = use_tempo self._use_temphum = use_temphum @@ -81,6 +83,7 @@ def __init__( self._temp_hum_id = None self._current_date = None self._local_time = None + self._local_date = None self._current_pricing_details = None self._night_pricing_details = None self._day_pricing_details = None @@ -91,6 +94,9 @@ def __init__( self._kwh_hc_night = None self._kwh_hc_ns = None self._kwh_hp_ns = None + self._last_kwh = None + self._last_kwh_hc_ns = None + self._last_kwh_hp_ns = None self._tempo_hc_blue = None self._tempo_hp_blue = None self._tempo_hc_white = None @@ -102,6 +108,7 @@ def __init__( self._outdoor_temp = None self._indoor_hum = None self._outdoor_hum = None + self._last_consumption_measure = None #67 fix self._status = APIStatus.INIT @@ -115,6 +122,11 @@ def current_date(self) -> datetime.date: """Return the native value of the sensor.""" return self._current_date + @property + def local_date(self) -> datetime.time: + """Return the native value of the sensor.""" + return self._local_date + @property def local_time(self) -> datetime.time: """Return the native value of the sensor.""" @@ -155,6 +167,21 @@ def kwh_hp_ns(self) -> int: """Return the native value of the sensor.""" return self._kwh_hp_ns + @property + def last_kwh(self) -> int: + """Return the native value of the sensor.""" + return self._last_kwh + + @property + def last_kwh_hc_ns(self) -> int: + """Return the native value of the sensor.""" + return self._last_kwh_hc_ns + + @property + def last_kwh_hp_ns(self) -> int: + """Return the native value of the sensor.""" + return self._last_kwh_hp_ns + @property def tempo_hc_blue(self) -> int: """Return the native value of the sensor.""" @@ -210,6 +237,11 @@ def outdoor_hum(self) -> int: """Return the native value of the sensor.""" return self._outdoor_hum + @property + def last_consumption_measure(self) -> int: + """Return the native value of the sensor.""" + return self._last_consumption_measure + def has_day_changed(self, datetime1, datetime2): """Compare two dates and return if day has changed.""" # Extract date components (year, month, day) @@ -224,6 +256,7 @@ async def async_get_date_time(self) -> any: # Get the current date self._current_date = datetime.date.today() paris_tz = pytz.timezone('Europe/Paris') + self._local_date = datetime.datetime.now(paris_tz).date() self._local_time = datetime.datetime.now(paris_tz).time() return @@ -235,7 +268,7 @@ async def async_get_data(self) -> None: if self._gateway_id is None: await self.async_get_gatewaydata() - previous_date = self._current_date + previous_local_date = self._local_date previous_local_time = self._local_time await self.async_get_date_time() if self._current_pricingzone is None or ( @@ -259,7 +292,7 @@ async def async_get_data(self) -> None: # Retrieving Night HC night_time = datetime.time(1, 0, 0) await self.async_get_pricing_details(is_current=False, - specific_date=self._current_date, + specific_date=self._local_date, specific_time=night_time) self._kwh_hc_night = await self.async_get_powerstat(self._night_pricing_details) if self._night_pricing_details == "HC Bleu": @@ -273,7 +306,7 @@ async def async_get_data(self) -> None: # Retrieving Day HP day_time = datetime.time(14, 0, 0) await self.async_get_pricing_details(is_current=False, - specific_date=self._current_date, + specific_date=self._local_date, specific_time=day_time) kwh_hp = await self.async_get_powerstat(self._day_pricing_details) if self._day_pricing_details == "HP Bleu": @@ -284,8 +317,8 @@ async def async_get_data(self) -> None: self._tempo_hp_red = kwh_hp else: #68 fix Tempo sensors not being reset when day changes - date1 = datetime.datetime(previous_date.year, previous_date.month, previous_date.day, previous_local_time.hour, previous_local_time.minute, previous_local_time.second) - date2 = datetime.datetime(self._current_date.year, self._current_date.month, self._current_date.day, self._local_time.hour, self._local_time.minute, self._local_time.second) + date1 = datetime.datetime(previous_local_date.year, previous_local_date.month, previous_local_date.day, previous_local_time.hour, previous_local_time.minute, previous_local_time.second) + date2 = datetime.datetime(self._local_date.year, self._local_date.month, self._local_date.day, self._local_time.hour, self._local_time.minute, self._local_time.second) if self.has_day_changed(date1, date2) is True: self._kwh_hc_night = None self._tempo_hc_blue = None @@ -297,6 +330,8 @@ async def async_get_data(self) -> None: await self.async_get_realtime_conso() + if self._use_last_measure is True: + await self.async_get_last_measure() await self.async_get_kwhstat() if self._use_temphum is True: await self.async_get_tempstat() @@ -370,6 +405,27 @@ async def async_get_realtime_conso(self) -> any: # "Something really wrong happened!" # ) from exception + async def async_get_last_measure(self) -> any: + """Get Ecojoko last measure.""" + try: + if self._cookies is None: + LOGGER.debug("Pas de cookies") + # raise exception + if self._gateway_id is None: + LOGGER.debug("Pas de gateway") + # TOTO raise exception + if self._power_meter_id is None: + LOGGER.debug("Pas de power meter") + # TOTO raise exception + return await self._last_measure_wrapper() + except Exception: # pylint: disable=broad-except + return + # except Exception as exception: # pylint: disable=broad-except + # raise LittleMonkeyApiClientError( + # "Something really wrong happened!" + # ) from exception + + async def async_get_kwhstat(self) -> any: """Get Ecojoko kwhstat.""" try: @@ -463,12 +519,14 @@ async def _cookiesapi_wrapper( data=data ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) self._cookies = response.cookies - response.raise_for_status() - return await response.json() + #response.raise_for_status() + return except asyncio.TimeoutError as exception: LOGGER.error("API Cookies timeout error") @@ -496,6 +554,8 @@ async def _gatewayapi_wrapper(self) -> any: cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) @@ -519,8 +579,8 @@ async def _gatewayapi_wrapper(self) -> any: if item["device_type"] == "POWER_METER": self._power_meter_id = item["device_id"] - response.raise_for_status() - return await response.json() + #response.raise_for_status() + return except asyncio.TimeoutError as exception: LOGGER.error("API Gateway timeout error") @@ -548,7 +608,7 @@ async def _pricing_details_wrapper(self, # Retrieve current Tempo pricing # Format the date as 'YYYY-MM-DD' if specific_date is None: - formatted_date = self._current_date.strftime('%Y-%m-%d') + formatted_date = self._local_date.strftime('%Y-%m-%d') else: #67 fix formatted_date = specific_date.strftime('%Y-%m-%d') @@ -568,6 +628,8 @@ async def _pricing_details_wrapper(self, cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) @@ -592,11 +654,11 @@ async def _pricing_details_wrapper(self, self._evening_pricing_details = pricing_details else: LOGGER.debug("PAS DE PRICING DETAILS") - response.raise_for_status() - return await response.json() + #response.raise_for_status() + return except asyncio.TimeoutError as exception: - LOGGER.error("API Pricing Details timeout error: %s") + LOGGER.error("API Pricing Details timeout error") raise LittleMonkeyApiClientCommunicationError( "Timeout error fetching information", ) from exception @@ -623,14 +685,16 @@ async def _realtimeconso_wrapper(self) -> any: cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) if "application/json" in response.headers.get("Content-Type", ""): value_json = await response.json() self._realtime_conso = value_json['real_time']['value'] - response.raise_for_status() - return await response.json() + #response.raise_for_status() + return except asyncio.TimeoutError as exception: LOGGER.error("API Realtime timeout error") @@ -648,6 +712,67 @@ async def _realtimeconso_wrapper(self) -> any: "Something really wrong happened!" ) from exception + async def _last_measure_wrapper(self) -> any: + """Get last measure from the API.""" + try: + # Format the date as 'YYYY-MM-DD' + formatted_date = self._local_date.strftime('%Y-%m-%d') + LOGGER.debug("DATE COURANTE: %s", formatted_date) + url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._power_meter_id}/powerstat/d4/{formatted_date}" + LOGGER.debug("URL: %s", url) + # formatted_date = formatted_date + self._local_time.strftime('%H:%M') + # url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._power_meter_id}/powerstat/h/{formatted_date}" + async with async_timeout.timeout(CONF_API_TIMEOUT): + response = await self._session.get( + url=url, + headers=self._headers, + cookies=self._cookies, + ) + if response.status in (401, 403): + #71 bug fix + self._cookies = None + raise LittleMonkeyApiClientAuthenticationError( + "Invalid credentials", + ) + if "application/json" in response.headers.get("Content-Type", ""): + value_json = await response.json() + if "data" in value_json['stat']: + if len(value_json['stat']['data']) > 1: + self._last_consumption_measure = value_json['stat']['data'][-1]['value'] + else: + # LOGGER.debug("UNE SEULE VALEUR: %s", value_json) + self._last_consumption_measure = value_json['stat']['data']['value'] + + if "period" in value_json['stat']: + self._last_kwh = value_json['stat']['period']['kwh'] + # LOGGER.warning("REPONSE ECOJOKO: %s", value_json) + if self._use_hchp is True: + self._last_kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] + self._last_kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] + else: + LOGGER.debug("NE RETOURNE PAS DE HC/HP") + + #response.raise_for_status() + return #await response.json() + + except asyncio.TimeoutError as exception: + LOGGER.error("API Last Measure timeout error") + raise LittleMonkeyApiClientCommunicationError( + "Timeout error fetching information", + ) from exception + except (aiohttp.ClientError, socket.gaierror) as exception: + LOGGER.error("API Last Measure client error: %s", exception) + raise LittleMonkeyApiClientCommunicationError( + "Error fetching information", + ) from exception + except Exception as exception: # pylint: disable=broad-except + LOGGER.error("API Last Measure other error: %s", exception) + #traceback.print_exc() + raise LittleMonkeyApiClientError( + "Something really wrong happened!" + ) from exception + + async def _kwhstat_wrapper(self) -> any: """Get kwhstat from the API.""" try: @@ -659,67 +784,76 @@ async def _kwhstat_wrapper(self) -> any: cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) if "application/json" in response.headers.get("Content-Type", ""): value_json = await response.json() - self._kwh = value_json['stat']['period']['kwh'] - # LOGGER.warning("REPONSE ECOJOKO: %s", value_json) - if self._use_hchp is True: - self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] - self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] - else: - LOGGER.debug("NE RETOURNE PAS DE HC/HP") - if self._use_tempo is True: - self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] - self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] - #63 - if self._current_pricing_details == "HC Bleu": - if self.current_pricingzone == PricingZone.HC_EVENING: - if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: - self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night - elif self._current_pricing_details == self._night_pricing_details: + if "period" in value_json['stat']: + self._kwh = value_json['stat']['period']['kwh'] + # LOGGER.warning("REPONSE ECOJOKO: %s", value_json) + if self._use_hchp is True: + self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] + self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] + else: + LOGGER.debug("NE RETOURNE PAS DE HC/HP") + if self._use_tempo is True: + self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] + self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] + #63 + if self._current_pricing_details == "HC Bleu": + if self.current_pricingzone == PricingZone.HC_EVENING: + if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night + elif self._current_pricing_details == self._night_pricing_details: + if self._kwh_hc_ns >= 0: + self._tempo_hc_blue = self._kwh_hc_ns + # else: + # self._tempo_hc_blue = self._kwh_hc_ns + else: self._tempo_hc_blue = self._kwh_hc_ns - # else: - # self._tempo_hc_blue = self._kwh_hc_ns - else: - self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night = self._kwh_hc_ns - elif self._current_pricing_details == "HP Bleu": - self._tempo_hp_blue = self._kwh_hp_ns - elif self._current_pricing_details == "HC Blanc": - if self.current_pricingzone == PricingZone.HC_EVENING: - if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: - self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night - elif self._current_pricing_details == self._night_pricing_details: + self._kwh_hc_night = self._kwh_hc_ns + elif self._current_pricing_details == "HP Bleu": + self._tempo_hp_blue = self._kwh_hp_ns + elif self._current_pricing_details == "HC Blanc": + if self.current_pricingzone == PricingZone.HC_EVENING: + if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night + elif self._current_pricing_details == self._night_pricing_details: + if self._kwh_hc_ns >= 0: + self._tempo_hc_white = self._kwh_hc_ns + # else: + # self._tempo_hc_white = self._kwh_hc_ns + else: self._tempo_hc_white = self._kwh_hc_ns - # else: - # self._tempo_hc_white = self._kwh_hc_ns - else: - self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night = self._kwh_hc_ns - elif self._current_pricing_details == "HP Blanc": - self._tempo_hp_white = self._kwh_hp_ns - elif self._current_pricing_details == "HC Rouge": - if self.current_pricingzone == PricingZone.HC_EVENING: - if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: - self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night - elif self._current_pricing_details == self._night_pricing_details: + self._kwh_hc_night = self._kwh_hc_ns + elif self._current_pricing_details == "HP Blanc": + self._tempo_hp_white = self._kwh_hp_ns + elif self._current_pricing_details == "HC Rouge": + if self.current_pricingzone == PricingZone.HC_EVENING: + if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night + elif self._current_pricing_details == self._night_pricing_details: + if self._kwh_hc_ns >= 0: + self._tempo_hc_red = self._kwh_hc_ns + # else: + # self._tempo_hc_red = self._kwh_hc_ns + else: self._tempo_hc_red = self._kwh_hc_ns - # else: - # self._tempo_hc_red = self._kwh_hc_ns - else: - self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night = self._kwh_hc_ns - elif self._current_pricing_details == "HP Rouge": - self._tempo_hp_red = self._kwh_hp_ns - if self._use_prod is True: - self._kwh_prod = -float(value_json['stat']['period']['kwh_prod']) - else: - LOGGER.debug("NE RETOURNE PAS DE PROD") - response.raise_for_status() - return await response.json() + self._kwh_hc_night = self._kwh_hc_ns + elif self._current_pricing_details == "HP Rouge": + self._tempo_hp_red = self._kwh_hp_ns + if self._use_prod is True: + self._kwh_prod = -float(value_json['stat']['period']['kwh_prod']) + else: + LOGGER.debug("NE RETOURNE PAS DE PROD") + #response.raise_for_status() + return except asyncio.TimeoutError as exception: LOGGER.error("API KWHSTAT timeout error") @@ -741,11 +875,8 @@ async def _kwhstat_wrapper(self) -> any: async def _tempstat_wrapper(self) -> any: """Get tempstat from the API.""" try: - #59 bug fix - # Get the current date - current_date = datetime.date.today() # Format the date as 'YYYY-MM-DD' - formatted_date = current_date.strftime('%Y-%m-%d') + formatted_date = self._local_date.strftime('%Y-%m-%d') url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._temp_hum_id}/tempstat/d4/{formatted_date}" async with async_timeout.timeout(CONF_API_TIMEOUT): response = await self._session.get( @@ -754,20 +885,23 @@ async def _tempstat_wrapper(self) -> any: cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) if "application/json" in response.headers.get("Content-Type", ""): value_json = await response.json() - if len(value_json['stat']['data']) > 1: - self._indoor_temp = value_json['stat']['data'][-1]['value'] - self._outdoor_temp = value_json['stat']['data'][-1]['ext_value'] - else: - # LOGGER.debug("TEMP UNE SEULE VALEUR: %s", value_json) - self._indoor_temp = value_json['stat']['data']['value'] - self._outdoor_temp = value_json['stat']['data']['ext_value'] - response.raise_for_status() - return await response.json() + if "data" in value_json['stat']: + if len(value_json['stat']['data']) > 1: + self._indoor_temp = value_json['stat']['data'][-1]['value'] + self._outdoor_temp = value_json['stat']['data'][-1]['ext_value'] + else: + # LOGGER.debug("TEMP UNE SEULE VALEUR: %s", value_json) + self._indoor_temp = value_json['stat']['data']['value'] + self._outdoor_temp = value_json['stat']['data']['ext_value'] + #response.raise_for_status() + return except asyncio.TimeoutError as exception: LOGGER.error("API TEMPSTAT timeout error") @@ -788,11 +922,8 @@ async def _tempstat_wrapper(self) -> any: async def _humstat_wrapper(self) -> any: """Get humstat from the API.""" try: - #59 bug fix - # Get the current date - current_date = datetime.date.today() # Format the date as 'YYYY-MM-DD' - formatted_date = current_date.strftime('%Y-%m-%d') + formatted_date = self._local_date.strftime('%Y-%m-%d') url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._temp_hum_id}/humstat/d4/{formatted_date}" async with async_timeout.timeout(CONF_API_TIMEOUT): response = await self._session.get( @@ -801,20 +932,23 @@ async def _humstat_wrapper(self) -> any: cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) if "application/json" in response.headers.get("Content-Type", ""): value_json = await response.json() - if len(value_json['stat']['data']) > 1: - self._indoor_hum = value_json['stat']['data'][-1]['value'] - self._outdoor_hum = value_json['stat']['data'][-1]['ext_value'] - else: - # LOGGER.debug("HUM UNE SEULE VALEUR: %s", value_json) - self._indoor_hum = value_json['stat']['data']['value'] - self._outdoor_hum = value_json['stat']['data']['ext_value'] - response.raise_for_status() - return await response.json() + if "data" in value_json['stat']: + if len(value_json['stat']['data']) > 1: + self._indoor_hum = value_json['stat']['data'][-1]['value'] + self._outdoor_hum = value_json['stat']['data'][-1]['ext_value'] + else: + # LOGGER.debug("HUM UNE SEULE VALEUR: %s", value_json) + self._indoor_hum = value_json['stat']['data']['value'] + self._outdoor_hum = value_json['stat']['data']['ext_value'] + #response.raise_for_status() + return except asyncio.TimeoutError as exception: LOGGER.error("API HUMSTAT timeout error") @@ -836,10 +970,8 @@ async def _powerstat_wrapper(self, pricing_details) -> any: """Get powerstat from the API.""" try: result = None - # Get the current date - current_date = datetime.date.today() # Format the date as 'YYYY-MM-DD' - formatted_date = current_date.strftime('%Y-%m-%d') + formatted_date = self._local_date.strftime('%Y-%m-%d') url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._power_meter_id}/powerstat/w/{formatted_date}" async with async_timeout.timeout(CONF_API_TIMEOUT): response = await self._session.get( @@ -848,18 +980,22 @@ async def _powerstat_wrapper(self, pricing_details) -> any: cookies=self._cookies, ) if response.status in (401, 403): + #71 bug fix + self._cookies = None raise LittleMonkeyApiClientAuthenticationError( "Invalid credentials", ) + result = None if "application/json" in response.headers.get("Content-Type", ""): value_json = await response.json() - week_day = self._current_date.weekday() - if len(value_json['stat']['data']) > week_day: - for subconscomption in value_json['stat']['data'][week_day]['subconsumption']: - if subconscomption['label'] == pricing_details: - result = subconscomption['kwh'] - break - return result + if "data" in value_json['stat']: + week_day = self._local_date.weekday() + if len(value_json['stat']['data']) > week_day: + for subconscomption in value_json['stat']['data'][week_day]['subconsumption']: + if subconscomption['label'] == pricing_details: + result = subconscomption['kwh'] + break + return result except asyncio.TimeoutError as exception: LOGGER.error("API HUMSTAT timeout error") diff --git a/custom_components/little_monkey/config_flow.py b/custom_components/little_monkey/config_flow.py index fbe1304..38cacd9 100644 --- a/custom_components/little_monkey/config_flow.py +++ b/custom_components/little_monkey/config_flow.py @@ -22,6 +22,7 @@ DOMAIN, POLL_INTERVAL, DEFAULT_POLL_INTERVAL, + CONF_USE_LAST_MEASURE_FEATURE, CONF_USE_HCHP_FEATURE, CONF_USE_TEMPO_FEATURE, CONF_USE_TEMPHUM_FEATURE, @@ -48,6 +49,9 @@ def _get_data_schema(config_entry: config_entries.ConfigEntry | None = None) -> type=selector.TextSelectorType.PASSWORD ), ), + vol.Optional( + CONF_USE_LAST_MEASURE_FEATURE, default=False, + ): cv.boolean, vol.Optional( CONF_USE_HCHP_FEATURE, default=False, ): cv.boolean, @@ -97,6 +101,9 @@ def _get_data_schema(config_entry: config_entries.ConfigEntry | None = None) -> type=selector.TextSelectorType.PASSWORD ), ), + vol.Optional( + CONF_USE_LAST_MEASURE_FEATURE, default=config_entry.data.get(CONF_USE_LAST_MEASURE_FEATURE), + ): cv.boolean, vol.Optional( CONF_USE_HCHP_FEATURE, default=config_entry.data.get(CONF_USE_HCHP_FEATURE), ): cv.boolean, @@ -151,6 +158,7 @@ async def async_step_user( await self._get_cookies( username=user_input[CONF_USERNAME], password=user_input[CONF_PASSWORD], + use_last_measure=user_input[CONF_USE_LAST_MEASURE_FEATURE], use_hchp=user_input[CONF_USE_HCHP_FEATURE], use_tempo=user_input[CONF_USE_TEMPO_FEATURE], use_temphum=user_input[CONF_USE_TEMPHUM_FEATURE], @@ -178,10 +186,11 @@ async def async_step_user( errors=_errors, ) - async def _get_cookies(self, username: str, password: str, use_hchp: bool, use_temphum: bool, use_tempo: bool, use_prod: bool) -> None: + async def _get_cookies(self, username: str, password: str, use_last_measure: bool, use_hchp: bool, use_temphum: bool, use_tempo: bool, use_prod: bool) -> None: client = LittleMonkeyApiClient( username=username, password=password, + use_last_measure=use_last_measure, use_hchp=use_hchp, use_tempo=use_tempo, use_temphum=use_temphum, @@ -223,6 +232,7 @@ async def async_step_init( client = await self._get_cookies( username=user_input[CONF_USERNAME], password=user_input[CONF_PASSWORD], + use_last_measure=user_input[CONF_USE_LAST_MEASURE_FEATURE], use_hchp=user_input[CONF_USE_HCHP_FEATURE], use_tempo=user_input[CONF_USE_TEMPO_FEATURE], use_temphum=user_input[CONF_USE_TEMPHUM_FEATURE], @@ -246,10 +256,11 @@ async def async_step_init( errors=self._errors, ) - async def _get_cookies(self, username: str, password: str, use_hchp: bool, use_tempo: bool, use_temphum: bool, use_prod: bool) -> LittleMonkeyApiClient: + async def _get_cookies(self, username: str, password: str, use_last_measure: bool, use_hchp: bool, use_tempo: bool, use_temphum: bool, use_prod: bool) -> LittleMonkeyApiClient: client = LittleMonkeyApiClient( username=username, password=password, + use_last_measure=use_last_measure, use_hchp=use_hchp, use_tempo=use_tempo, use_temphum=use_temphum, diff --git a/custom_components/little_monkey/const.py b/custom_components/little_monkey/const.py index 50a689a..9f89845 100644 --- a/custom_components/little_monkey/const.py +++ b/custom_components/little_monkey/const.py @@ -7,7 +7,7 @@ DOMAIN = "little_monkey" MANUFACTURER = "Jean-Marc Cruvellier" MODEL = "Ecojoko" -VERSION = "1.0.1" +VERSION = "1.1.0" ATTRIBUTION = "Data provided by https://service.ecojoko.com//" POLL_INTERVAL = "poll_interval" DEFAULT_POLL_INTERVAL = "5" @@ -17,6 +17,7 @@ CONF_USE_TEMPO_FEATURE = "use_tempo_feature" CONF_USE_TEMPHUM_FEATURE = "use_temphum_feature" CONF_USE_PROD_FEATURE = "use_prod_feature" +CONF_USE_LAST_MEASURE_FEATURE = "use_last_measure_feature" CONF_LANG = 'lang' DEFAULT_LANG = 'fr-FR' # Language Supported Codes diff --git a/custom_components/little_monkey/coordinator.py b/custom_components/little_monkey/coordinator.py index 2d5e7e4..f49ac4d 100644 --- a/custom_components/little_monkey/coordinator.py +++ b/custom_components/little_monkey/coordinator.py @@ -79,6 +79,10 @@ async def _async_update_data(self): "grid_consumption": self.client.kwh, "hc_grid_consumption": self.client.kwh_hc_ns, "hp_grid_consumption": self.client.kwh_hp_ns, + "last_consumption_measure": self.client.last_consumption_measure, + "last_grid_consumption_measure": self.client.last_kwh, + "last_hc_grid_consumption_measure": self.client.last_kwh_hc_ns, + "last_hp_grid_consumption_measure": self.client.last_kwh_hp_ns, "blue_hc_grid_consumption": self.client.tempo_hc_blue, "blue_hp_grid_consumption": self.client.tempo_hp_blue, "white_hc_grid_consumption": self.client.tempo_hc_white, diff --git a/custom_components/little_monkey/little_monkey_translations/en.json b/custom_components/little_monkey/little_monkey_translations/en.json index 6f1ee2f..32f9f1a 100644 --- a/custom_components/little_monkey/little_monkey_translations/en.json +++ b/custom_components/little_monkey/little_monkey_translations/en.json @@ -13,5 +13,9 @@ "indoor_temp": "Indoor Temperature", "outdoor_temp": "Outdoor Temperature", "indoor_hum": "Indoor Humidity", - "outdoor_hum": "Outdoor Humidity" + "outdoor_hum": "Outdoor Humidity", + "last_consumption_measure": "Last Consumption Measure", + "last_grid_consumption_measure": "Last Grid Consumption Measure", + "last_hc_grid_consumption_measure": "Last HC Grid Consumption Measure", + "last_hp_grid_consumption_measure": "Last HP Grid Consumption Measure" } \ No newline at end of file diff --git a/custom_components/little_monkey/little_monkey_translations/fr.json b/custom_components/little_monkey/little_monkey_translations/fr.json index e7274b3..d62b693 100644 --- a/custom_components/little_monkey/little_monkey_translations/fr.json +++ b/custom_components/little_monkey/little_monkey_translations/fr.json @@ -13,5 +13,9 @@ "indoor_temp": "Température Intérieure", "outdoor_temp": "Température Extérieure", "indoor_hum": "Humidité Intérieure", - "outdoor_hum": "Humidité Extérieure" + "outdoor_hum": "Humidité Extérieure", + "last_consumption_measure": "Consommation Dernière Mesure", + "last_grid_consumption_measure": "Consommation Réseau Dernière Mesure", + "last_hc_grid_consumption_measure": "Consommation HC Réseau Dernière Mesure", + "last_hp_grid_consumption_measure": "Consommation HP Réseau Dernière Mesure" } \ No newline at end of file diff --git a/custom_components/little_monkey/manifest.json b/custom_components/little_monkey/manifest.json index af2c680..7b0b6ec 100644 --- a/custom_components/little_monkey/manifest.json +++ b/custom_components/little_monkey/manifest.json @@ -5,9 +5,9 @@ "@jmcruvellier" ], "config_flow": true, - "documentation": "https://github.com/jmcruvellier/little_monkey/blob/v1.0.1/README.md", + "documentation": "https://github.com/jmcruvellier/little_monkey/blob/v1.1.0/README.md", "integration_type": "device", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/jmcruvellier/little_monkey/issues", - "version": "1.0.1" + "version": "1.1.0" } \ No newline at end of file diff --git a/custom_components/little_monkey/sensor.py b/custom_components/little_monkey/sensor.py index c62b5cb..230d067 100644 --- a/custom_components/little_monkey/sensor.py +++ b/custom_components/little_monkey/sensor.py @@ -13,7 +13,8 @@ CONF_USE_HCHP_FEATURE, CONF_USE_TEMPO_FEATURE, CONF_USE_TEMPHUM_FEATURE, - CONF_USE_PROD_FEATURE + CONF_USE_PROD_FEATURE, + CONF_USE_LAST_MEASURE_FEATURE ) async def async_setup_entry(hass, config_entry, async_add_entities): @@ -148,4 +149,30 @@ async def async_setup_entry(hass, config_entry, async_add_entities): PERCENTAGE, "mdi:water")) + # Last Consumption Measure sensor + if config_entry.data.get(CONF_USE_LAST_MEASURE_FEATURE) is True: + main_device.add_child_entity(EcojokoSensor( + main_device, + "last_consumption_measure", + SensorStateClass.MEASUREMENT, + SensorDeviceClass.POWER, + UnitOfPower.WATT, + "mdi:flash")) + # Last HC/HP grid consumption measure sensors + if config_entry.data.get(CONF_USE_HCHP_FEATURE) is True: + main_device.add_child_entity(EcojokoSensor( + main_device, + "last_hc_grid_consumption_measure", + SensorStateClass.TOTAL_INCREASING, + SensorDeviceClass.ENERGY, + UnitOfEnergy.KILO_WATT_HOUR, + "mdi:lightning-bolt")) + main_device.add_child_entity(EcojokoSensor( + main_device, + "last_hp_grid_consumption_measure", + SensorStateClass.TOTAL_INCREASING, + SensorDeviceClass.ENERGY, + UnitOfEnergy.KILO_WATT_HOUR, + "mdi:lightning-bolt")) + async_add_entities([main_device] + main_device.child_entities) diff --git a/custom_components/little_monkey/translations/en.json b/custom_components/little_monkey/translations/en.json index 5b2df8b..ddfab7a 100644 --- a/custom_components/little_monkey/translations/en.json +++ b/custom_components/little_monkey/translations/en.json @@ -8,6 +8,7 @@ "name": "Name", "username": "Username", "password": "Password", + "use_last_measure_feature": "Last measure sensors", "use_hchp_feature": "HP/HC sensors", "use_tempo_feature": "Tempo Blue/White/Red sensors", "use_temphum_feature": "Humidity and temperature sensors", @@ -30,6 +31,7 @@ "name": "Name", "username": "Username", "password": "Password", + "use_last_measure_feature": "Last measure sensors", "use_hchp_feature": "HP/HC sensors", "use_tempo_feature": "Tempo Blue/White/Red sensors", "use_temphum_feature": "Humidity and temperature sensors", diff --git a/custom_components/little_monkey/translations/fr.json b/custom_components/little_monkey/translations/fr.json index 7b79f08..3d0b9b4 100644 --- a/custom_components/little_monkey/translations/fr.json +++ b/custom_components/little_monkey/translations/fr.json @@ -8,6 +8,7 @@ "name": "Nom", "username": "Nom d'utilisateur", "password": "Mot de passe", + "use_last_measure_feature": "Capteurs de dernières mesures", "use_hchp_feature": "Capteurs HP/HC", "use_tempo_feature": "Capteurs Tempo Bleu/Blanc/Rouge", "use_temphum_feature": "Capteurs d'humidité et de température", @@ -30,6 +31,7 @@ "name": "Nom", "username": "Nom d'utilisateur", "password": "Mot de passe", + "use_last_measure_feature": "Capteurs de dernières mesures", "use_hchp_feature": "Capteurs HP/HC", "use_tempo_feature": "Capteurs Tempo Bleu/Blanc/Rouge", "use_temphum_feature": "Capteurs d'humidité et de température", From 3207e01bed6b634f51347c3e7fe5b9bf9fb7201a Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sun, 4 Feb 2024 10:36:33 +0000 Subject: [PATCH 4/8] Changelog + Info + Code cleaning --- CHANGELOG.md | 6 ++++ custom_components/little_monkey/api.py | 3 -- info.md | 44 +++++++++++++++----------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a1f42..67b152a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.1.0 +- New sensors based on ecojoko Measurements APIs +- Some performance improvement and minor bug fixes +- Bug fix [#58](https://github.com/jmcruvellier/little_monkey/issues/58) +- Bug fix [#71](https://github.com/jmcruvellier/little_monkey/issues/71) + ## 1.0.1 - Bug fix [#67](https://github.com/jmcruvellier/little_monkey/issues/67) diff --git a/custom_components/little_monkey/api.py b/custom_components/little_monkey/api.py index d0d90b9..d176558 100644 --- a/custom_components/little_monkey/api.py +++ b/custom_components/little_monkey/api.py @@ -620,7 +620,6 @@ async def _pricing_details_wrapper(self, local_time = specific_time formatted_date = formatted_date + specific_time.strftime('%H:%M') url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._power_meter_id}/powerstat/h/{formatted_date}" - #LOGGER.debug("URL: %s", url) async with async_timeout.timeout(CONF_API_TIMEOUT): response = await self._session.get( url=url, @@ -717,9 +716,7 @@ async def _last_measure_wrapper(self) -> any: try: # Format the date as 'YYYY-MM-DD' formatted_date = self._local_date.strftime('%Y-%m-%d') - LOGGER.debug("DATE COURANTE: %s", formatted_date) url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._power_meter_id}/powerstat/d4/{formatted_date}" - LOGGER.debug("URL: %s", url) # formatted_date = formatted_date + self._local_time.strftime('%H:%M') # url = ECOJOKO_GATEWAY_URL + f"/{self._gateway_id}/device/{self._power_meter_id}/powerstat/h/{formatted_date}" async with async_timeout.timeout(CONF_API_TIMEOUT): diff --git a/info.md b/info.md index 353918c..4df1f27 100644 --- a/info.md +++ b/info.md @@ -1,3 +1,8 @@ +[![CodeQL](https://github.com/jmcruvellier/little_monkey/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/github-code-scanning/codeql) +[![HACS](https://github.com/jmcruvellier/little_monkey/actions/workflows/hacs.yaml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/hacs.yaml) +[![hassfest](https://github.com/jmcruvellier/little_monkey/actions/workflows/hassfest.yaml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/hassfest.yaml) +[![Validate](https://github.com/jmcruvellier/little_monkey/actions/workflows/validate.yml/badge.svg)](https://github.com/jmcruvellier/little_monkey/actions/workflows/validate.yml) + ![](/custom_components/little_monkey/res/logo_small.png) # Little Monkey / Petit Singe @@ -5,24 +10,27 @@ Cette intégration vous permet de récupérer les informations collectées par v Elle intègre dans Home Assistant les capteurs suivants: -* Consommation Temps Réel (Puissance en W) -* Consommation Réseau (Energie en kWh) -* Si vous avez un contrat d'énergie HC/HP - - Consommation HC Réseau (Energie en kWh) - - Consommation HP Réseau (Energie en kWh) - - Si c'est un contrat Tempo: - - Consommation HC Bleu Réseau (Energie en kWh) - - Consommation HP Bleu Réseau (Energie en kWh) - - Consommation HC Blanc Réseau (Energie en kWh) - - Consommation HP Blanc Réseau (Energie en kWh) - - Consommation HC Rouge Réseau (Energie en kWh) - - Consommation HP Rouge Réseau (Energie en kWh) -* Si vous êtes producteur d'énergie grâce à des panneaux photovoltaïques et possesseur d'un capteur ecojoko ancienne génération: - - Surplus de Production (Energie en kWh) -* Température Intérieure (en °C) -* Température Extérieure (en °C) -* Humidité Intérieure (en %) -* Humidité Extérieure (en %) +| Version | Capteur | Type | Unité | Disponibilité | Commentaire | +| ------- | ------- | ---- | ----- | ------------- | ----------- | +| 1.0.0 | Consommation Temps Réel | Puissance | W | Permanent | | +| 1.0.0 | Consommation Réseau | Energie | kWh | Permanent | | +| 1.0.0 | Consommation HC Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie incluant les HC/HP | +| 1.0.0 | Consommation HP Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie incluant les HC/HP | +| 1.0.0 | Consommation HC Bleu Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HP Bleu Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HC Blanc Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HP Blanc Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HC Rouge Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Consommation HP Rouge Réseau | Energie | kWh | Optionnel | Si vous avez un contrat d'énergie Tempo | +| 1.0.0 | Surplus de Production | Energie | kWh | Optionnel | Si vous êtes producteur d'énergie grâce à des panneaux photovoltaïques et possesseur d'un capteur ecojoko ancienne génération | +| 1.0.0 | Température Intérieure | Température | °C | Optionnel | | +| 1.0.0 | Température Extérieure | Température | °C | Optionnel | | +| 1.0.0 | Humidité Intérieure | Humidité | % | Optionnel | | +| 1.0.0 | Humidité Extérieure | Humidité | % | Optionnel | | +| 1.1.0 | Consommation Dernière Mesure | Puissance | W | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | +| 1.1.0 | Consommation Réseau Dernière Mesure | Energie | kWh | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | +| 1.1.0 | Consommation HC Réseau Dernière Mesure | Energie | kWh | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | +| 1.1.0 | Consommation HP Réseau Dernière Mesure | Energie | kWh | Optionnel | Dernière valeur retournée dans la section Mesures de l'application ecojoko | > [!IMPORTANT] > Si vous êtes un utilisateur régulier de l'application ecojoko©️, vous n'êtes pas sans savoir que le petit singe glisse souvent sur sa peau de banane. **Cette __intégration non-officielle__ dépend des APIs d'ecojoko©️ et n'est donc pas responsable en cas d'indisponibilité de vos donnés.** From 96583fcdbd074a197c8876a4ed6c7b65e5e9e5eb Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sun, 4 Feb 2024 13:38:00 +0000 Subject: [PATCH 5/8] Firmware data refresh --- custom_components/little_monkey/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/little_monkey/api.py b/custom_components/little_monkey/api.py index d176558..2229c1c 100644 --- a/custom_components/little_monkey/api.py +++ b/custom_components/little_monkey/api.py @@ -327,7 +327,7 @@ async def async_get_data(self) -> None: self._tempo_hp_white = None self._tempo_hc_red = None self._tempo_hp_red = None - + await self.async_get_gatewaydata() await self.async_get_realtime_conso() if self._use_last_measure is True: From 39ccf7065381d71314058fdb6c7e552ee6450d0d Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sun, 4 Feb 2024 14:18:43 +0000 Subject: [PATCH 6/8] Cleanup --- custom_components/little_monkey/__init__.py | 4 +-- custom_components/little_monkey/api.py | 39 --------------------- 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/custom_components/little_monkey/__init__.py b/custom_components/little_monkey/__init__.py index 5f8a09e..3d8b12b 100644 --- a/custom_components/little_monkey/__init__.py +++ b/custom_components/little_monkey/__init__.py @@ -23,11 +23,11 @@ from .coordinator import LittleMonkeyDataUpdateCoordinator def get_boolean(array, index): - """Read the value with a default of False if the key is not found""" + """Read the value with a default of False if the key is not found.""" return array.get(index, False) def get_string(array, index): - """Read the value with a default of empty string if the key is not found""" + """Read the value with a default of empty string if the key is not found.""" return array.get(index, "") diff --git a/custom_components/little_monkey/api.py b/custom_components/little_monkey/api.py index 2229c1c..18ed529 100644 --- a/custom_components/little_monkey/api.py +++ b/custom_components/little_monkey/api.py @@ -2,7 +2,6 @@ from __future__ import annotations #import traceback - import asyncio import datetime import socket @@ -27,13 +26,11 @@ class LittleMonkeyApiClientError(Exception): """Exception to indicate a general API error.""" - class LittleMonkeyApiClientCommunicationError( LittleMonkeyApiClientError ): """Exception to indicate a communication error.""" - class LittleMonkeyApiClientAuthenticationError( LittleMonkeyApiClientError ): @@ -380,10 +377,6 @@ async def async_get_pricing_details(self, specific_time=specific_time) except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception async def async_get_realtime_conso(self) -> any: """Get Ecojoko realtime consumption.""" @@ -400,10 +393,6 @@ async def async_get_realtime_conso(self) -> any: return await self._realtimeconso_wrapper() except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception async def async_get_last_measure(self) -> any: """Get Ecojoko last measure.""" @@ -420,11 +409,6 @@ async def async_get_last_measure(self) -> any: return await self._last_measure_wrapper() except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception - async def async_get_kwhstat(self) -> any: """Get Ecojoko kwhstat.""" @@ -441,10 +425,6 @@ async def async_get_kwhstat(self) -> any: return await self._kwhstat_wrapper() except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception async def async_get_tempstat(self) -> any: """Get Ecojoko tempstat.""" @@ -461,10 +441,6 @@ async def async_get_tempstat(self) -> any: return await self._tempstat_wrapper() except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception async def async_get_humstat(self) -> any: """Get Ecojoko humstat.""" @@ -481,10 +457,6 @@ async def async_get_humstat(self) -> any: return await self._humstat_wrapper() except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception async def async_get_powerstat(self, pricing_details) -> any: """Get Ecojoko powerstat.""" @@ -501,10 +473,6 @@ async def async_get_powerstat(self, pricing_details) -> any: return await self._powerstat_wrapper(pricing_details) except Exception: # pylint: disable=broad-except return - # except Exception as exception: # pylint: disable=broad-except - # raise LittleMonkeyApiClientError( - # "Something really wrong happened!" - # ) from exception async def _cookiesapi_wrapper( self, @@ -790,7 +758,6 @@ async def _kwhstat_wrapper(self) -> any: value_json = await response.json() if "period" in value_json['stat']: self._kwh = value_json['stat']['period']['kwh'] - # LOGGER.warning("REPONSE ECOJOKO: %s", value_json) if self._use_hchp is True: self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] @@ -808,8 +775,6 @@ async def _kwhstat_wrapper(self) -> any: elif self._current_pricing_details == self._night_pricing_details: if self._kwh_hc_ns >= 0: self._tempo_hc_blue = self._kwh_hc_ns - # else: - # self._tempo_hc_blue = self._kwh_hc_ns else: self._tempo_hc_blue = self._kwh_hc_ns self._kwh_hc_night = self._kwh_hc_ns @@ -823,8 +788,6 @@ async def _kwhstat_wrapper(self) -> any: elif self._current_pricing_details == self._night_pricing_details: if self._kwh_hc_ns >= 0: self._tempo_hc_white = self._kwh_hc_ns - # else: - # self._tempo_hc_white = self._kwh_hc_ns else: self._tempo_hc_white = self._kwh_hc_ns self._kwh_hc_night = self._kwh_hc_ns @@ -838,8 +801,6 @@ async def _kwhstat_wrapper(self) -> any: elif self._current_pricing_details == self._night_pricing_details: if self._kwh_hc_ns >= 0: self._tempo_hc_red = self._kwh_hc_ns - # else: - # self._tempo_hc_red = self._kwh_hc_ns else: self._tempo_hc_red = self._kwh_hc_ns self._kwh_hc_night = self._kwh_hc_ns From a717cd9131d7886edf84576c7fca1f7f876496a5 Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sun, 4 Feb 2024 14:24:10 +0000 Subject: [PATCH 7/8] Cleanup --- custom_components/little_monkey/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/little_monkey/api.py b/custom_components/little_monkey/api.py index 18ed529..5ac6af8 100644 --- a/custom_components/little_monkey/api.py +++ b/custom_components/little_monkey/api.py @@ -761,8 +761,8 @@ async def _kwhstat_wrapper(self) -> any: if self._use_hchp is True: self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] - else: - LOGGER.debug("NE RETOURNE PAS DE HC/HP") + # else: + # LOGGER.debug("NE RETOURNE PAS DE HC/HP") if self._use_tempo is True: self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] @@ -808,8 +808,8 @@ async def _kwhstat_wrapper(self) -> any: self._tempo_hp_red = self._kwh_hp_ns if self._use_prod is True: self._kwh_prod = -float(value_json['stat']['period']['kwh_prod']) - else: - LOGGER.debug("NE RETOURNE PAS DE PROD") + # else: + # LOGGER.debug("NE RETOURNE PAS DE PROD") #response.raise_for_status() return From 077bb70c857c42f389fd96c18bcee4058c1b2856 Mon Sep 17 00:00:00 2001 From: Jean-Marc Cruvellier Date: Sun, 4 Feb 2024 14:36:34 +0000 Subject: [PATCH 8/8] Cleanup --- custom_components/little_monkey/api.py | 120 +++++++++++++++++-------- 1 file changed, 81 insertions(+), 39 deletions(-) diff --git a/custom_components/little_monkey/api.py b/custom_components/little_monkey/api.py index 5ac6af8..5df194e 100644 --- a/custom_components/little_monkey/api.py +++ b/custom_components/little_monkey/api.py @@ -737,6 +737,47 @@ async def _last_measure_wrapper(self) -> any: "Something really wrong happened!" ) from exception + async def Tempo(self) -> any: + """Tempo data analysis.""" + if self._current_pricing_details == "HC Bleu": + if self.current_pricingzone == PricingZone.HC_EVENING: + if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night + elif self._current_pricing_details == self._night_pricing_details: + if self._kwh_hc_ns >= 0: + self._tempo_hc_blue = self._kwh_hc_ns + else: + self._tempo_hc_blue = self._kwh_hc_ns + self._kwh_hc_night = self._kwh_hc_ns + elif self._current_pricing_details == "HP Bleu": + self._tempo_hp_blue = self._kwh_hp_ns + elif self._current_pricing_details == "HC Blanc": + if self.current_pricingzone == PricingZone.HC_EVENING: + if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night + elif self._current_pricing_details == self._night_pricing_details: + if self._kwh_hc_ns >= 0: + self._tempo_hc_white = self._kwh_hc_ns + else: + self._tempo_hc_white = self._kwh_hc_ns + self._kwh_hc_night = self._kwh_hc_ns + elif self._current_pricing_details == "HP Blanc": + self._tempo_hp_white = self._kwh_hp_ns + elif self._current_pricing_details == "HC Rouge": + if self.current_pricingzone == PricingZone.HC_EVENING: + if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night + elif self._current_pricing_details == self._night_pricing_details: + if self._kwh_hc_ns >= 0: + self._tempo_hc_red = self._kwh_hc_ns + else: + self._tempo_hc_red = self._kwh_hc_ns + self._kwh_hc_night = self._kwh_hc_ns + elif self._current_pricing_details == "HP Rouge": + self._tempo_hp_red = self._kwh_hp_ns async def _kwhstat_wrapper(self) -> any: """Get kwhstat from the API.""" @@ -767,45 +808,46 @@ async def _kwhstat_wrapper(self) -> any: self._kwh_hp_ns = value_json['stat']['period']['kwh_hp_ns'] self._kwh_hc_ns = value_json['stat']['period']['kwh_hc_ns'] #63 - if self._current_pricing_details == "HC Bleu": - if self.current_pricingzone == PricingZone.HC_EVENING: - if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: - if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: - self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night - elif self._current_pricing_details == self._night_pricing_details: - if self._kwh_hc_ns >= 0: - self._tempo_hc_blue = self._kwh_hc_ns - else: - self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night = self._kwh_hc_ns - elif self._current_pricing_details == "HP Bleu": - self._tempo_hp_blue = self._kwh_hp_ns - elif self._current_pricing_details == "HC Blanc": - if self.current_pricingzone == PricingZone.HC_EVENING: - if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: - if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: - self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night - elif self._current_pricing_details == self._night_pricing_details: - if self._kwh_hc_ns >= 0: - self._tempo_hc_white = self._kwh_hc_ns - else: - self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night = self._kwh_hc_ns - elif self._current_pricing_details == "HP Blanc": - self._tempo_hp_white = self._kwh_hp_ns - elif self._current_pricing_details == "HC Rouge": - if self.current_pricingzone == PricingZone.HC_EVENING: - if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: - if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: - self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night - elif self._current_pricing_details == self._night_pricing_details: - if self._kwh_hc_ns >= 0: - self._tempo_hc_red = self._kwh_hc_ns - else: - self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night = self._kwh_hc_ns - elif self._current_pricing_details == "HP Rouge": - self._tempo_hp_red = self._kwh_hp_ns + await self.Tempo() + # if self._current_pricing_details == "HC Bleu": + # if self.current_pricingzone == PricingZone.HC_EVENING: + # if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + # if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + # self._tempo_hc_blue = self._kwh_hc_ns - self._kwh_hc_night + # elif self._current_pricing_details == self._night_pricing_details: + # if self._kwh_hc_ns >= 0: + # self._tempo_hc_blue = self._kwh_hc_ns + # else: + # self._tempo_hc_blue = self._kwh_hc_ns + # self._kwh_hc_night = self._kwh_hc_ns + # elif self._current_pricing_details == "HP Bleu": + # self._tempo_hp_blue = self._kwh_hp_ns + # elif self._current_pricing_details == "HC Blanc": + # if self.current_pricingzone == PricingZone.HC_EVENING: + # if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + # if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + # self._tempo_hc_white = self._kwh_hc_ns - self._kwh_hc_night + # elif self._current_pricing_details == self._night_pricing_details: + # if self._kwh_hc_ns >= 0: + # self._tempo_hc_white = self._kwh_hc_ns + # else: + # self._tempo_hc_white = self._kwh_hc_ns + # self._kwh_hc_night = self._kwh_hc_ns + # elif self._current_pricing_details == "HP Blanc": + # self._tempo_hp_white = self._kwh_hp_ns + # elif self._current_pricing_details == "HC Rouge": + # if self.current_pricingzone == PricingZone.HC_EVENING: + # if self._kwh_hc_night is not None and self._current_pricing_details != self._night_pricing_details: + # if (self._kwh_hc_ns - self._kwh_hc_night) >= 0: + # self._tempo_hc_red = self._kwh_hc_ns - self._kwh_hc_night + # elif self._current_pricing_details == self._night_pricing_details: + # if self._kwh_hc_ns >= 0: + # self._tempo_hc_red = self._kwh_hc_ns + # else: + # self._tempo_hc_red = self._kwh_hc_ns + # self._kwh_hc_night = self._kwh_hc_ns + # elif self._current_pricing_details == "HP Rouge": + # self._tempo_hp_red = self._kwh_hp_ns if self._use_prod is True: self._kwh_prod = -float(value_json['stat']['period']['kwh_prod']) # else: