From c599ebbd4c29c9ea5f4dba06490db9c51c2f5fab Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Mercier Date: Sun, 14 May 2023 16:17:50 +0200 Subject: [PATCH] Feature/battery state (#46) --- README.md | 2 ++ .../mylight_systems/api/client.py | 29 +++++++++++++++++++ .../mylight_systems/api/const.py | 1 + custom_components/mylight_systems/const.py | 2 +- .../mylight_systems/coordinator.py | 14 +++++++-- custom_components/mylight_systems/sensor.py | 12 +++++++- 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 388f0e7..e0c06ce 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ _Integration to integrate with [MyLight Systems][mylight_systems]._ | `sensor.total_self_conso` | Self consumption. | % | :white_check_mark: | | `sensor.total_msb_charge` | My Smart Battery Charge. | W/h | :white_check_mark: | | `sensor.total_msb_discharge` | My Smart Battery Discharge. | W/h | :white_check_mark: | +| `sensor.battery_state` | Current battery state. | kW | :white_check_mark: | +| `sensor.total_green_energy` | Total power consumned (from the production) by you home. | W/h | :white_check_mark: | ## Installation diff --git a/custom_components/mylight_systems/api/client.py b/custom_components/mylight_systems/api/client.py index 2033072..17de892 100644 --- a/custom_components/mylight_systems/api/client.py +++ b/custom_components/mylight_systems/api/client.py @@ -16,6 +16,7 @@ DEVICES_URL, MEASURES_TOTAL_URL, PROFILE_URL, + STATES_URL, ) from .exceptions import ( CommunicationException, @@ -133,6 +134,7 @@ async def async_get_devices(self, auth_token: str) -> InstallationDevices: model.virtual_device_id = device["id"] if device["type"] == "bat": model.virtual_battery_id = device["id"] + model.virtual_battery_capacity = device["batteryCapacity"] if device["type"] == "mst": model.master_id = device["id"] model.master_report_period = device["reportPeriod"] @@ -165,3 +167,30 @@ async def async_get_measures_total( ) return measures + + async def async_get_battery_state( + self, auth_token: str, battery_id: str + ) -> Measure | None: + """Get battery state.""" + response = await self._execute_request( + "get", STATES_URL, params={"authToken": auth_token} + ) + + if response["status"] == "error": + if response["error"] == "not.authorized": + raise UnauthorizedException() + + measure: Measure = None + + for device in response["deviceStates"]: + if device["deviceId"] == battery_id: + for state in device["sensorStates"]: + if state["sensorId"] == battery_id + "-soc": + measure = Measure( + state["measure"]["type"], + state["measure"]["value"], + state["measure"]["unit"], + ) + return measure + + return measure diff --git a/custom_components/mylight_systems/api/const.py b/custom_components/mylight_systems/api/const.py index a6757e1..d87419b 100644 --- a/custom_components/mylight_systems/api/const.py +++ b/custom_components/mylight_systems/api/const.py @@ -6,3 +6,4 @@ PROFILE_URL: str = "/api/profile" DEVICES_URL: str = "/api/devices" MEASURES_TOTAL_URL: str = "/api/measures/total" +STATES_URL: str = "/api/states" diff --git a/custom_components/mylight_systems/const.py b/custom_components/mylight_systems/const.py index cd67720..2bf8817 100644 --- a/custom_components/mylight_systems/const.py +++ b/custom_components/mylight_systems/const.py @@ -9,7 +9,7 @@ NAME = "MyLight Systems" DOMAIN = "mylight_systems" PLATFORMS = [Platform.SENSOR] -VERSION = "0.0.2" +VERSION = "0.0.5" COORDINATOR = "coordinator" ATTRIBUTION = "Data provided by https://www.mylight-systems.com/" SCAN_INTERVAL_IN_MINUTES = 15 diff --git a/custom_components/mylight_systems/coordinator.py b/custom_components/mylight_systems/coordinator.py index 01e4f19..ed420f1 100644 --- a/custom_components/mylight_systems/coordinator.py +++ b/custom_components/mylight_systems/coordinator.py @@ -23,6 +23,7 @@ ) from .const import ( CONF_GRID_TYPE, + CONF_VIRTUAL_BATTERY_ID, CONF_VIRTUAL_DEVICE_ID, DOMAIN, LOGGER, @@ -41,6 +42,7 @@ class MyLightSystemsCoordinatorData(NamedTuple): msb_charge: Measure msb_discharge: Measure green_energy: Measure + battery_state: Measure # https://developers.home-assistant.io/docs/integration_fetching_data#coordinated-single-api-poll-for-data-for-all-entities @@ -72,6 +74,9 @@ async def _async_update_data(self) -> MyLightSystemsCoordinatorData: password = self.config_entry.data[CONF_PASSWORD] grid_type = self.config_entry.data[CONF_GRID_TYPE] device_id = self.config_entry.data[CONF_VIRTUAL_DEVICE_ID] + virtual_battery_id = self.config_entry.data[ + CONF_VIRTUAL_BATTERY_ID + ] await self.authenticate_user(email, password) @@ -79,6 +84,10 @@ async def _async_update_data(self) -> MyLightSystemsCoordinatorData: self.__auth_token, grid_type, device_id ) + battery_state = await self.client.async_get_battery_state( + self.__auth_token, virtual_battery_id + ) + return MyLightSystemsCoordinatorData( produced_energy=self.find_measure_by_type( result, "produced_energy" @@ -95,9 +104,8 @@ async def _async_update_data(self) -> MyLightSystemsCoordinatorData: msb_discharge=self.find_measure_by_type( result, "msb_discharge" ), - green_energy=self.find_measure_by_type( - result, "green_energy" - ), + green_energy=self.find_measure_by_type(result, "green_energy"), + battery_state=battery_state, ) except ( UnauthorizedException, diff --git a/custom_components/mylight_systems/sensor.py b/custom_components/mylight_systems/sensor.py index dce4ce1..d6c4916 100644 --- a/custom_components/mylight_systems/sensor.py +++ b/custom_components/mylight_systems/sensor.py @@ -10,7 +10,7 @@ SensorEntityDescription, SensorStateClass, ) -from homeassistant.const import PERCENTAGE, UnitOfEnergy +from homeassistant.const import PERCENTAGE, POWER_KILO_WATT, UnitOfEnergy from .const import DOMAIN from .coordinator import ( @@ -126,6 +126,16 @@ class MyLightSensorEntityDescription( if data.green_energy is not None else 0, ), + MyLightSensorEntityDescription( + key="battery_state", + name="Battery state", + icon="mdi:battery", + native_unit_of_measurement=POWER_KILO_WATT, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda data: round(data.battery_state.value / 36e2 / 1e3, 2) + if data.battery_state is not None + else 0, + ), )