diff --git a/.coveragerc b/.coveragerc
index 0a1430cce91236cfbabfe961aae6e3652987e651..8a83b98873ed9cb2f5bdaf9b4f0f66ab618950ac 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -602,6 +602,10 @@ omit =
     homeassistant/components/juicenet/number.py
     homeassistant/components/juicenet/sensor.py
     homeassistant/components/juicenet/switch.py
+    homeassistant/components/justnimbus/const.py
+    homeassistant/components/justnimbus/coordinator.py
+    homeassistant/components/justnimbus/entity.py
+    homeassistant/components/justnimbus/sensor.py
     homeassistant/components/kaiterra/*
     homeassistant/components/kankun/switch.py
     homeassistant/components/keba/*
diff --git a/CODEOWNERS b/CODEOWNERS
index 292879aabeaf91257cc3a7fa70e2bb537a2fb6cf..c92ad3b5ba97be5d1c0cebb4d892351eeff582e6 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -555,6 +555,8 @@ build.json @home-assistant/supervisor
 /tests/components/jewish_calendar/ @tsvi
 /homeassistant/components/juicenet/ @jesserockz
 /tests/components/juicenet/ @jesserockz
+/homeassistant/components/justnimbus/ @kvanzuijlen
+/tests/components/justnimbus/ @kvanzuijlen
 /homeassistant/components/kaiterra/ @Michsior14
 /homeassistant/components/kaleidescape/ @SteveEasley
 /tests/components/kaleidescape/ @SteveEasley
diff --git a/homeassistant/components/justnimbus/__init__.py b/homeassistant/components/justnimbus/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..695faa4f529b28834becb0bd8a9b530f312e16d0
--- /dev/null
+++ b/homeassistant/components/justnimbus/__init__.py
@@ -0,0 +1,25 @@
+"""The JustNimbus integration."""
+from __future__ import annotations
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.core import HomeAssistant
+
+from .const import DOMAIN, PLATFORMS
+from .coordinator import JustNimbusCoordinator
+
+
+async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
+    """Set up JustNimbus from a config entry."""
+    coordinator = JustNimbusCoordinator(hass=hass, entry=entry)
+    await coordinator.async_config_entry_first_refresh()
+
+    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
+    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
+    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):
+        hass.data[DOMAIN].pop(entry.entry_id)
+    return unload_ok
diff --git a/homeassistant/components/justnimbus/config_flow.py b/homeassistant/components/justnimbus/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..bb55b1852b8158b74393b2e9b8f522d0f45aec3d
--- /dev/null
+++ b/homeassistant/components/justnimbus/config_flow.py
@@ -0,0 +1,60 @@
+"""Config flow for JustNimbus integration."""
+from __future__ import annotations
+
+import logging
+from typing import Any
+
+import justnimbus
+import voluptuous as vol
+
+from homeassistant import config_entries
+from homeassistant.const import CONF_CLIENT_ID
+from homeassistant.data_entry_flow import FlowResult
+from homeassistant.helpers import config_validation as cv
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+STEP_USER_DATA_SCHEMA = vol.Schema(
+    {
+        vol.Required(CONF_CLIENT_ID): cv.string,
+    },
+)
+
+
+class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
+    """Handle a config flow for JustNimbus."""
+
+    VERSION = 1
+
+    async def async_step_user(
+        self, user_input: dict[str, Any] | None = None
+    ) -> FlowResult:
+        """Handle the initial step."""
+        if user_input is None:
+            return self.async_show_form(
+                step_id="user", data_schema=STEP_USER_DATA_SCHEMA
+            )
+
+        errors = {}
+
+        await self.async_set_unique_id(user_input[CONF_CLIENT_ID])
+        self._abort_if_unique_id_configured()
+
+        client = justnimbus.JustNimbusClient(client_id=user_input[CONF_CLIENT_ID])
+        try:
+            await self.hass.async_add_executor_job(client.get_data)
+        except justnimbus.InvalidClientID:
+            errors["base"] = "invalid_auth"
+        except justnimbus.JustNimbusError:
+            errors["base"] = "cannot_connect"
+        except Exception:  # pylint: disable=broad-except
+            _LOGGER.exception("Unexpected exception")
+            errors["base"] = "unknown"
+        else:
+            return self.async_create_entry(title="JustNimbus", data=user_input)
+
+        return self.async_show_form(
+            step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
+        )
diff --git a/homeassistant/components/justnimbus/const.py b/homeassistant/components/justnimbus/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..cf3d4ef825f0d6e3cb2b245b31334408c487c461
--- /dev/null
+++ b/homeassistant/components/justnimbus/const.py
@@ -0,0 +1,13 @@
+"""Constants for the JustNimbus integration."""
+
+from typing import Final
+
+from homeassistant.const import Platform
+
+DOMAIN = "justnimbus"
+
+VOLUME_FLOW_RATE_LITERS_PER_MINUTE: Final = "L/min"
+
+PLATFORMS = [
+    Platform.SENSOR,
+]
diff --git a/homeassistant/components/justnimbus/coordinator.py b/homeassistant/components/justnimbus/coordinator.py
new file mode 100644
index 0000000000000000000000000000000000000000..606cea0e922f68e4ea996c92be87b741d6109c8d
--- /dev/null
+++ b/homeassistant/components/justnimbus/coordinator.py
@@ -0,0 +1,34 @@
+"""JustNimbus coordinator."""
+from __future__ import annotations
+
+from datetime import timedelta
+import logging
+
+import justnimbus
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import CONF_CLIENT_ID
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class JustNimbusCoordinator(DataUpdateCoordinator[justnimbus.JustNimbusModel]):
+    """Data update coordinator."""
+
+    def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
+        """Initialize the coordinator."""
+        super().__init__(
+            hass,
+            _LOGGER,
+            name=DOMAIN,
+            update_interval=timedelta(minutes=1),
+        )
+        self._client = justnimbus.JustNimbusClient(client_id=entry.data[CONF_CLIENT_ID])
+
+    async def _async_update_data(self) -> justnimbus.JustNimbusModel:
+        """Fetch the latest data from the source."""
+        return await self.hass.async_add_executor_job(self._client.get_data)
diff --git a/homeassistant/components/justnimbus/entity.py b/homeassistant/components/justnimbus/entity.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9ea5ba115165d391fab9bd83b62f670cbffae7e
--- /dev/null
+++ b/homeassistant/components/justnimbus/entity.py
@@ -0,0 +1,37 @@
+"""Base Entity for JustNimbus sensors."""
+from __future__ import annotations
+
+import justnimbus
+
+from homeassistant.components.sensor import SensorEntity
+from homeassistant.helpers import update_coordinator
+from homeassistant.helpers.entity import DeviceInfo
+
+from . import JustNimbusCoordinator
+from .const import DOMAIN
+
+
+class JustNimbusEntity(
+    update_coordinator.CoordinatorEntity[justnimbus.JustNimbusModel],
+    SensorEntity,
+):
+    """Defines a base JustNimbus entity."""
+
+    def __init__(
+        self,
+        *,
+        device_id: str,
+        coordinator: JustNimbusCoordinator,
+    ) -> None:
+        """Initialize the JustNimbus entity."""
+        super().__init__(coordinator=coordinator)
+        self._attr_device_info = DeviceInfo(
+            identifiers={(DOMAIN, device_id)},
+            name="JustNimbus Sensor",
+            manufacturer="JustNimbus",
+        )
+
+    @property
+    def available(self) -> bool:
+        """Return device availability."""
+        return super().available and getattr(self.coordinator.data, "error_code") == 0
diff --git a/homeassistant/components/justnimbus/manifest.json b/homeassistant/components/justnimbus/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..ca25832df00a061fe12cc5ee1fd19b61ddaffb91
--- /dev/null
+++ b/homeassistant/components/justnimbus/manifest.json
@@ -0,0 +1,9 @@
+{
+  "domain": "justnimbus",
+  "name": "JustNimbus",
+  "config_flow": true,
+  "documentation": "https://www.home-assistant.io/integrations/justnimbus",
+  "requirements": ["justnimbus==0.6.0"],
+  "codeowners": ["@kvanzuijlen"],
+  "iot_class": "cloud_polling"
+}
diff --git a/homeassistant/components/justnimbus/sensor.py b/homeassistant/components/justnimbus/sensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..6041f84e25a9a03cd3c7badfec1b37b687be839f
--- /dev/null
+++ b/homeassistant/components/justnimbus/sensor.py
@@ -0,0 +1,193 @@
+"""Support for the JustNimbus platform."""
+from __future__ import annotations
+
+from collections.abc import Callable
+from dataclasses import dataclass
+from typing import Any
+
+from homeassistant.components.sensor import (
+    SensorDeviceClass,
+    SensorEntityDescription,
+    SensorStateClass,
+)
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import (
+    CONF_CLIENT_ID,
+    PRESSURE_BAR,
+    TEMP_CELSIUS,
+    VOLUME_LITERS,
+)
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers.entity import EntityCategory
+from homeassistant.helpers.entity_platform import AddEntitiesCallback
+from homeassistant.helpers.typing import StateType
+
+from . import JustNimbusCoordinator
+from .const import DOMAIN, VOLUME_FLOW_RATE_LITERS_PER_MINUTE
+from .entity import JustNimbusEntity
+
+
+@dataclass
+class JustNimbusEntityDescriptionMixin:
+    """Mixin for required keys."""
+
+    value_fn: Callable[[JustNimbusCoordinator], Any]
+
+
+@dataclass
+class JustNimbusEntityDescription(
+    SensorEntityDescription, JustNimbusEntityDescriptionMixin
+):
+    """Describes JustNimbus sensor entity."""
+
+
+SENSOR_TYPES = (
+    JustNimbusEntityDescription(
+        key="pump_flow",
+        name="Pump flow",
+        icon="mdi:pump",
+        native_unit_of_measurement=VOLUME_FLOW_RATE_LITERS_PER_MINUTE,
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.pump_flow,
+    ),
+    JustNimbusEntityDescription(
+        key="drink_flow",
+        name="Drink flow",
+        icon="mdi:water-pump",
+        native_unit_of_measurement=VOLUME_FLOW_RATE_LITERS_PER_MINUTE,
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.drink_flow,
+    ),
+    JustNimbusEntityDescription(
+        key="pump_pressure",
+        name="Pump pressure",
+        native_unit_of_measurement=PRESSURE_BAR,
+        device_class=SensorDeviceClass.PRESSURE,
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.pump_pressure,
+    ),
+    JustNimbusEntityDescription(
+        key="pump_starts",
+        name="Pump starts",
+        icon="mdi:restart",
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.pump_starts,
+    ),
+    JustNimbusEntityDescription(
+        key="pump_hours",
+        name="Pump hours",
+        icon="mdi:clock",
+        device_class=SensorDeviceClass.DURATION,
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.pump_hours,
+    ),
+    JustNimbusEntityDescription(
+        key="reservoir_temp",
+        name="Reservoir Temperature",
+        native_unit_of_measurement=TEMP_CELSIUS,
+        device_class=SensorDeviceClass.TEMPERATURE,
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.reservoir_temp,
+    ),
+    JustNimbusEntityDescription(
+        key="reservoir_content",
+        name="Reservoir content",
+        icon="mdi:car-coolant-level",
+        native_unit_of_measurement=VOLUME_LITERS,
+        state_class=SensorStateClass.MEASUREMENT,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.reservoir_content,
+    ),
+    JustNimbusEntityDescription(
+        key="total_saved",
+        name="Total saved",
+        icon="mdi:water-opacity",
+        native_unit_of_measurement=VOLUME_LITERS,
+        state_class=SensorStateClass.TOTAL_INCREASING,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.total_saved,
+    ),
+    JustNimbusEntityDescription(
+        key="total_replenished",
+        name="Total replenished",
+        icon="mdi:water",
+        native_unit_of_measurement=VOLUME_LITERS,
+        state_class=SensorStateClass.TOTAL_INCREASING,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.total_replenished,
+    ),
+    JustNimbusEntityDescription(
+        key="error_code",
+        name="Error code",
+        icon="mdi:bug",
+        entity_registry_enabled_default=False,
+        native_unit_of_measurement="",
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.error_code,
+    ),
+    JustNimbusEntityDescription(
+        key="totver",
+        name="Total use",
+        icon="mdi:chart-donut",
+        native_unit_of_measurement=VOLUME_LITERS,
+        state_class=SensorStateClass.TOTAL_INCREASING,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.totver,
+    ),
+    JustNimbusEntityDescription(
+        key="reservoir_content_max",
+        name="Max reservoir content",
+        icon="mdi:waves",
+        native_unit_of_measurement=VOLUME_LITERS,
+        state_class=SensorStateClass.TOTAL,
+        entity_category=EntityCategory.DIAGNOSTIC,
+        value_fn=lambda coordinator: coordinator.data.reservoir_content_max,
+    ),
+)
+
+
+async def async_setup_entry(
+    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
+) -> None:
+    """Set up the JustNimbus sensor."""
+    coordinator: JustNimbusCoordinator = hass.data[DOMAIN][entry.entry_id]
+    async_add_entities(
+        JustNimbusSensor(
+            device_id=entry.data[CONF_CLIENT_ID],
+            description=description,
+            coordinator=coordinator,
+        )
+        for description in SENSOR_TYPES
+    )
+
+
+class JustNimbusSensor(
+    JustNimbusEntity,
+):
+    """Implementation of the JustNimbus sensor."""
+
+    def __init__(
+        self,
+        *,
+        device_id: str,
+        description: JustNimbusEntityDescription,
+        coordinator: JustNimbusCoordinator,
+    ) -> None:
+        """Initialize the sensor."""
+        self.entity_description: JustNimbusEntityDescription = description
+        super().__init__(
+            device_id=device_id,
+            coordinator=coordinator,
+        )
+        self._attr_unique_id = f"{device_id}_{description.key}"
+
+    @property
+    def native_value(self) -> StateType:
+        """Return sensor state."""
+        return self.entity_description.value_fn(self.coordinator)
diff --git a/homeassistant/components/justnimbus/strings.json b/homeassistant/components/justnimbus/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..609b1425e93783558fd0fc6bd31c16a1113c565e
--- /dev/null
+++ b/homeassistant/components/justnimbus/strings.json
@@ -0,0 +1,19 @@
+{
+  "config": {
+    "step": {
+      "user": {
+        "data": {
+          "client_id": "Client ID"
+        }
+      }
+    },
+    "error": {
+      "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
+      "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
+      "unknown": "[%key:common::config_flow::error::unknown%]"
+    },
+    "abort": {
+      "already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
+    }
+  }
+}
diff --git a/homeassistant/components/justnimbus/translations/en.json b/homeassistant/components/justnimbus/translations/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..31443841e8abc5660b7231e0dce5fef1f3c05e62
--- /dev/null
+++ b/homeassistant/components/justnimbus/translations/en.json
@@ -0,0 +1,19 @@
+{
+    "config": {
+        "abort": {
+            "already_configured": "Device is already configured"
+        },
+        "error": {
+            "cannot_connect": "Failed to connect",
+            "invalid_auth": "Invalid authentication",
+            "unknown": "Unexpected error"
+        },
+        "step": {
+            "user": {
+                "data": {
+                    "client_id": "Client ID"
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index 597cb10fd7f8670cb97a3f57c15d70171337d2ef..d9da1ff20574f6875cd81a54e12f20f6df73e75a 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -183,6 +183,7 @@ FLOWS = {
         "izone",
         "jellyfin",
         "juicenet",
+        "justnimbus",
         "kaleidescape",
         "keenetic_ndms2",
         "kmtronic",
diff --git a/requirements_all.txt b/requirements_all.txt
index d2777a873f3441e4fb2c61686813118a6cf37f15..6b67daab6fdef2f17ff8a7f7e30ffea818f81ed1 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -925,6 +925,9 @@ jellyfin-apiclient-python==1.8.1
 # homeassistant.components.rest
 jsonpath==0.82
 
+# homeassistant.components.justnimbus
+justnimbus==0.6.0
+
 # homeassistant.components.kaiterra
 kaiterra-async-client==1.0.0
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 75a0c06acc9a490272c1830e673660e5f851c24a..0c80e40f5e91a70af94fc4e2315bfbc698acee24 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -675,6 +675,9 @@ jellyfin-apiclient-python==1.8.1
 # homeassistant.components.rest
 jsonpath==0.82
 
+# homeassistant.components.justnimbus
+justnimbus==0.6.0
+
 # homeassistant.components.konnected
 konnected==1.2.0
 
diff --git a/tests/components/justnimbus/__init__.py b/tests/components/justnimbus/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..46d872e08c690e2cdcacbca78fc0b69667a5954c
--- /dev/null
+++ b/tests/components/justnimbus/__init__.py
@@ -0,0 +1 @@
+"""Tests for the JustNimbus integration."""
diff --git a/tests/components/justnimbus/test_config_flow.py b/tests/components/justnimbus/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2cfb64d4c7f246e3f6cb72984448e1ad0d700fc
--- /dev/null
+++ b/tests/components/justnimbus/test_config_flow.py
@@ -0,0 +1,84 @@
+"""Test the JustNimbus config flow."""
+from unittest.mock import patch
+
+from justnimbus.exceptions import InvalidClientID, JustNimbusError
+import pytest
+
+from homeassistant import config_entries
+from homeassistant.components.justnimbus.const import DOMAIN
+from homeassistant.const import CONF_CLIENT_ID
+from homeassistant.core import HomeAssistant
+from homeassistant.data_entry_flow import FlowResultType
+
+
+async def test_form(hass: HomeAssistant) -> None:
+    """Test we get the form."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    assert result["type"] == FlowResultType.FORM
+    assert result["errors"] is None
+
+    await _set_up_justnimbus(hass=hass, flow_id=result["flow_id"])
+
+
+@pytest.mark.parametrize(
+    "side_effect,errors",
+    (
+        (
+            InvalidClientID(client_id="test_id"),
+            {"base": "invalid_auth"},
+        ),
+        (
+            JustNimbusError(),
+            {"base": "cannot_connect"},
+        ),
+    ),
+)
+async def test_form_errors(
+    hass: HomeAssistant,
+    side_effect: JustNimbusError,
+    errors: dict,
+) -> None:
+    """Test we handle errors."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    with patch(
+        "justnimbus.JustNimbusClient.get_data",
+        side_effect=side_effect,
+    ):
+        result2 = await hass.config_entries.flow.async_configure(
+            flow_id=result["flow_id"],
+            user_input={
+                CONF_CLIENT_ID: "test_id",
+            },
+        )
+
+    assert result2["type"] == FlowResultType.FORM
+    assert result2["errors"] == errors
+
+    await _set_up_justnimbus(hass=hass, flow_id=result["flow_id"])
+
+
+async def _set_up_justnimbus(hass: HomeAssistant, flow_id: str) -> None:
+    """Reusable successful setup of JustNimbus sensor."""
+    with patch("justnimbus.JustNimbusClient.get_data"), patch(
+        "homeassistant.components.justnimbus.async_setup_entry",
+        return_value=True,
+    ) as mock_setup_entry:
+        result2 = await hass.config_entries.flow.async_configure(
+            flow_id=flow_id,
+            user_input={
+                CONF_CLIENT_ID: "test_id",
+            },
+        )
+        await hass.async_block_till_done()
+
+    assert result2["type"] == FlowResultType.CREATE_ENTRY
+    assert result2["title"] == "JustNimbus"
+    assert result2["data"] == {
+        CONF_CLIENT_ID: "test_id",
+    }
+    assert len(mock_setup_entry.mock_calls) == 1