Repository: home-assistant/core
Pull request: Add option to specify Reolink Basic Service Port
<!--
  You are amazing! Thanks for contributing to our project!
  Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
-->
## Breaking change
<!--
  If your PR contains a breaking change for existing users, it is important
  to tell them what breaks, how to make it work again and why we did this.
  This piece of text is published with the release notes, so it helps if you
  write it towards our users, not us.
  Note: Remove this section if this PR is NOT a breaking change.
-->


## Proposed change
<!--
  Describe the big picture of your changes here to communicate to the
  maintainers why we should accept this pull request. If it fixes a bug
  or resolves a feature request, be sure to link to that issue in the
  additional information section.
-->
Add option to specify Reolink Basic Service Port/Baichuan port.
This port is basically always 9000, however if a user changes this deep in the settings of the reolink desktop client this can cause issues.
However as long as the HTTP(s) ports are enabled and active, the baichuan port will automatically be retrieved over the HTTP API protocol.

So this is only a issue if the HTTP(s) ports are closed (for instance after a firmware update) and the user changed the Baichuan port to something other then 9000. (normally the Baichuan protocol would be used to re-open the HTTP(s) port).

Basically this is a very very edge case, but still nice to be able to support it properly.
Up to now I have only encountered a single user that ever changed the baichuan port and prompted me to make these changes....

The baichuan port will automatically be updated in the config entry data for existing users or whenever a user changes the port (in this case a warning is logged 1x, the port should not change by itself).

## Type of change
<!--
  What type of change does your PR introduce to Home Assistant?
  NOTE: Please, check only 1! box!
  If your PR requires multiple boxes to be checked, you'll most likely need to
  split it into multiple PRs. This makes things easier and faster to code review.
-->

- [ ] Dependency upgrade
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New integration (thank you!)
- [x] New feature (which adds functionality to an existing integration)
- [ ] Deprecation (breaking change to happen in the future)
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [ ] Code quality improvements to existing code or addition of tests

## Additional information
<!--
  Details are important, and help maintainers processing your PR.
  Please be sure to fill out additional details, if applicable.
-->

- This PR fixes or closes issue: fixes https://github.com/starkillerOG/reolink_aio/issues/100
- This PR is related to issue: 
- Link to documentation pull request: Since this option will only be shown after a error occurs (and then has a description in place) I do not want to add it to the docs. It will only confuse regular users. 99.9% of users will not need this option and should leave it at the default port 9000.
- Link to developer documentation pull request: 
- Link to frontend pull request: 

## Checklist
<!--
  Put an `x` in the boxes that apply. You can also fill these out after
  creating the PR. If you're unsure about any of them, don't hesitate to ask.
  We're here to help! This is simply a reminder of what we are going to look
  for before merging your code.
-->

- [x] The code change is tested and works locally.
- [x] Local tests pass. **Your PR cannot be merged unless tests pass**
- [x] There is no commented out code in this PR.
- [x] I have followed the [development checklist][dev-checklist]
- [x] I have followed the [perfect PR recommendations][perfect-pr]
- [x] The code has been formatted using Ruff (`ruff format homeassistant tests`)
- [x] Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

- [x] Documentation added/updated for [www.home-assistant.io][docs-repository]

If the code communicates with devices, web services, or third-party tools:

- [x] The [manifest file][manifest-docs] has all fields filled out correctly.  
      Updated and included derived files by running: `python3 -m script.hassfest`.
- [x] New or updated dependencies have been added to `requirements_all.txt`.  
      Updated by running `python3 -m script.gen_requirements_all`.
- [x] For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

<!--
  This project is very active and we have a high turnover of pull requests.

  Unfortunately, the number of incoming pull requests is higher than what our
  reviewers can review and merge so there is a long backlog of pull requests
  waiting for review. You can help here!
  
  By reviewing another pull request, you will help raise the code quality of
  that pull request and the final review will be faster. This way the general
  pace of pull request reviews will go up and your wait time will go down.
  
  When picking a pull request to review, try to choose one that hasn't yet
  been reviewed.

  Thanks for helping out!
-->

To help with the load of incoming pull requests:

- [ ] I have reviewed two other [open pull requests][prs] in this repository.

[prs]: https://github.com/home-assistant/core/pulls?q=is%3Aopen+is%3Apr+-author%3A%40me+-draft%3Atrue+-label%3Awaiting-for-upstream+sort%3Acreated-desc+review%3Anone+-status%3Afailure

<!--
  Thank you for contributing <3

  Below, some useful links you could explore:
-->
[dev-checklist]: https://developers.home-assistant.io/docs/development_checklist/
[manifest-docs]: https://developers.home-assistant.io/docs/creating_integration_manifest/
[quality-scale]: https://developers.home-assistant.io/docs/integration_quality_scale_index/
[docs-repository]: https://github.com/home-assistant/home-assistant.io
[perfect-pr]: https://developers.home-assistant.io/docs/review-process/#creating-the-perfect-pr


Problem statement:
Baichuan Error with Custom (non 9000) port for Basic Service Port
**Describe the bug**
Starting around the time of the commit dated to October 17, 2024, I began to get regular disconnects in Home Assistant where I would need to re-authenticate my cameras. I assumed it would iron itself out given enough time, and found similar issues, but lately I realized the issue was unique. I did some digging in the codebase for the reolink_aio and I see that there is an update around the 17th of October, 2024 that introduced the Baichuan protocol implementation and it appears this port is auto-set to "9000". I was using a custom port for the basic service and this custom port functioned well for every other use case but introduces errors for my Home Assistant reolink integration.

**To Reproduce**
Set Basic Service Port to anything but 9000, restart Home Assistant - Pop up comes up stating re-authentication is necessary (not all the time, but over 75% of the time one of my two cameras fails, at least, in my estimation). Reauthentication completes successfully once I specify the https port manually but specification must happen each time.

**Expected behavior**
I would expect the integration to either extract the basic service port like it does the other ports, or else allow the end-user to set it and retain that port for future use.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Environment:**
Please provide useful information about your environment, like:
 - Reolink NVR/camera model
     No NVR/Reolink TrackMix WiFi (hardware no. IPC_529SD78MP)
 - NVR/camera firmware version
     No NVR/v3.0.0.4255_2411271222

**Additional context**
Home Assistant Log States things like:
```
Logger: homeassistant.config_entries
Source: config_entries.py:668
First occurred: 3:20:08 PM (1 occurrences)
Last logged: 3:20:08 PM
```
```
Config entry '******' for reolink integration could not authenticate: Host 192.168.1.**:****: Invalid credentials during login, 'Login has been locked, please try again later!'
```
(replaced port and masked IP with asterisks for privacy).

and

```
Logger: reolink_aio.api
Source: components/reolink/host.py:150
First occurred: 3:20:08 PM (1 occurrences)
Last logged: 3:20:08 PM

Host 192.168.1.**:****: Invalid credentials during login, 'Login has been locked, please try again later!'
```
(replaced port and masked IP with asterisks for privacy).

Though the logs appear to indicate an issue with the https port and credential login, I have a strong theory that it is related to the basic service port and that is causing the inaccurate obtaining of the https ports based off the wrong default basic service port being used as this didn't happen prior to the update regarding the Baichuan protocol and I have seen other logs related to Baichuan host previously (cannot find them now, unfortunately). Another alternative solution could be to more persistently store the https port specified and revert to it if the Baichuan protocol call fails, though this would seem to add more complexity than necessary for something that could be solved by having a way to manually specify the Basic Service Protocol port instead. As a temporary solution I reset my basic service port to port 9000, though I do not like the security implications of not being able to change it to a non-standard number.

Hints from issue discussion:
Today, I noticed another similar error that may be relevant but unrelated to the port number (as I left it at 9000 for the time being):

```
Logger: reolink_aio.baichuan.baichuan
Source: components/reolink/host.py:539
First occurred: 4:03:16 PM (1 occurrences)
Last logged: 4:03:16 PM

Baichuan host ***.***.*.**: keepalive loop seems to have stopped running, restarting
```
How did you even change the baichuan port?
I think this is fixed in the reolink firmware to port 9000.
Through the reolink mobile app and windows client it will not allow you to change that port.
You can set specific ports for HTTPS/HTTP but those are diffrent from the baichuan port.
This error `Config entry '******' for reolink integration could not authenticate: Host 192.168.1.**:****: Invalid credentials during login, 'Login has been locked, please try again later!'` indicates the integration has tried to many times and it hit the lockout and now the IP is blocked. Maybe I can do something about that, but I first need to understand which ports are set to what in your case.
> You can set specific ports for HTTPS/HTTP but those are diffrent from the baichuan port.

I am able to set the "Basic Service Port" value under the Windows client on my Reolink Windows App. You are right on not being able to change this on the mobile app, though. I assumed this was the Baichun port as it lined up with the 9000 value.

I can update more info on my ports later today, if that helps, but for the most part my cameras have largely remained connected after changing my Basic Service Port back to 9000.

Here are the most recent error logs, if they help:
```
Logger: reolink_aio.baichuan.baichuan
Source: runner.py:154
First occurred: February 2, 2025 at 2:00:00 AM (2 occurrences)
Last logged: February 2, 2025 at 2:00:01 AM

Baichuan host 192.168.1.***: lost event subscription after 22.71 s and failed to reestablished connection
Baichuan host 192.168.1.**: lost event subscription after 11.47 s and failed to reestablished connection
```

```
Logger: reolink_aio.api
Source: components/reolink/camera.py:148
First occurred: February 2, 2025 at 2:00:05 AM (8 occurrences)
Last logged: February 2, 2025 at 2:00:05 AM

Host 192.168.1.**:****: API error: API returned HTTP status ERROR code 502/Bad Gateway.
Error while logging out: API returned HTTP status ERROR code 502/Bad Gateway
Host 192.168.1.**:****: unknown exception "API error during login of host 192.168.1.**:****: API returned HTTP status ERROR code 502/Bad Gateway" occurred, traceback: Traceback (most recent call last): File "/usr/local/lib/python3.13/site-packages/reolink_aio/baichuan/baichuan.py", line 144, in send self._transport, self._protocol = await self._loop.create_connection( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lambda: BaichuanTcpClientProtocol(self._loop, self._host, self._push_callback, self._close_callback), self._host, self._port ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/usr/local/lib/python3.13/asyncio/base_events.py", line 1161, in create_connection raise exceptions[0] File "/usr/local/lib/python3.13/asyncio/base_events.py", line 1136, in create_connection sock = await self._connect_sock( ^^^^^^^^^^^^^^^^^^^^^^^^^ exceptions, addrinfo, laddr_infos) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.13/asyncio/base_events.py", line 1039, in _connect_sock await self.sock_connect(sock, address) File "/usr/local/lib/python3.13/asyncio/selector_events.py", line 641, in sock_connect return await fut ^^^^^^^^^ File "/usr/local/lib/python3.13/asyncio/selector_events.py", line 681, in _sock_connect_cb raise OSError(err, f'Connect call failed {address}') ConnectionRefusedError: [Errno 111] Connect call failed ('192.168.1.**', 9000) The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 1409, in _login_open_port await self.baichuan.get_ports() File "/usr/local/lib/python3.13/site-packages/reolink_aio/baichuan/baichuan.py", line 631, in get_ports mess = await self.send(cmd_id=37) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.13/site-packages/reolink_aio/baichuan/baichuan.py", line 106, in send await self.login() File "/usr/local/lib/python3.13/site-packages/reolink_aio/baichuan/baichuan.py", line 572, in login nonce = await self._get_nonce() ^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.13/site-packages/reolink_aio/baichuan/baichuan.py", line 365, in _get_nonce mess = await self.send(cmd_id=1, enc_type=EncType.BC, message_class="1465") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.13/site-packages/reolink_aio/baichuan/baichuan.py", line 150, in send raise ReolinkConnectionError(f"Baichuan host {self._host}: Connection error: {str(err)}") from err reolink_aio.exceptions.ReolinkConnectionError: Baichuan host 192.168.1.**: Connection error: [Errno 111] Connect call failed ('192.168.1.**', 9000) The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 5547, in send_chunk return await self.send(body, param, expected_response_type, retry) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 5385, in send return await self.send_chunk(body, param, expected_response_type, retry) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 5449, in send_chunk await self._login_open_port() File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 1413, in _login_open_port raise first_exc from exc File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 1402, in _login_open_port await self.login() File "/usr/local/lib/python3.13/site-packages/reolink_aio/api.py", line 1251, in login raise LoginError(f"API error during login of host {self._host}:{self._port}: {str(err)}") from err reolink_aio.exceptions.LoginError: API error during login of host 192.168.1.**:****: API returned HTTP status ERROR code 502/Bad Gateway
```

```
Logger: reolink_aio.baichuan.baichuan
Source: components/reolink/host.py:539
First occurred: 12:17:27 AM (1 occurrences)
Last logged: 12:17:27 AM

Baichuan host 192.168.1.**: keepalive loop seems to have stopped running, restarting
```
@WebDesignerlol damn you are right!
I remembered wrong: you can not disable the basic service port, but apperently you can change the port number.

Now I will need to make this configurable....

Alright will start working on this, but its going to take some time.
There are quite some places where changes will be needed.
Thank you! Take your time, I appreciate all the work you put into this!

Commit message: Add tests

Patch to review:
diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py
index 71ca54287404b..2489133841aed 100644
--- a/homeassistant/components/reolink/__init__.py
+++ b/homeassistant/components/reolink/__init__.py
@@ -28,7 +28,7 @@
 from homeassistant.helpers.typing import ConfigType
 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
 
-from .const import CONF_SUPPORTS_PRIVACY_MODE, CONF_USE_HTTPS, DOMAIN
+from .const import CONF_BC_PORT, CONF_SUPPORTS_PRIVACY_MODE, CONF_USE_HTTPS, DOMAIN
 from .exceptions import PasswordIncompatible, ReolinkException, UserNotAdmin
 from .host import ReolinkHost
 from .services import async_setup_services
@@ -100,6 +100,7 @@ async def async_setup_entry(
         or host.api.use_https != config_entry.data[CONF_USE_HTTPS]
         or host.api.supported(None, "privacy_mode")
         != config_entry.data.get(CONF_SUPPORTS_PRIVACY_MODE)
+        or host.api.baichuan.port != config_entry.data.get(CONF_BC_PORT)
     ):
         if host.api.port != config_entry.data[CONF_PORT]:
             _LOGGER.warning(
@@ -108,10 +109,21 @@ async def async_setup_entry(
                 config_entry.data[CONF_PORT],
                 host.api.port,
             )
+        if (
+            config_entry.data.get(CONF_BC_PORT, host.api.baichuan.port)
+            != host.api.baichuan.port
+        ):
+            _LOGGER.warning(
+                "Baichuan port of Reolink %s, changed from %s to %s",
+                host.api.nvr_name,
+                config_entry.data.get(CONF_BC_PORT),
+                host.api.baichuan.port,
+            )
         data = {
             **config_entry.data,
             CONF_PORT: host.api.port,
             CONF_USE_HTTPS: host.api.use_https,
+            CONF_BC_PORT: host.api.baichuan.port,
             CONF_SUPPORTS_PRIVACY_MODE: host.api.supported(None, "privacy_mode"),
         }
         hass.config_entries.async_update_entry(config_entry, data=data)
diff --git a/homeassistant/components/reolink/config_flow.py b/homeassistant/components/reolink/config_flow.py
index 7943cadef21b6..1d73be96a793f 100644
--- a/homeassistant/components/reolink/config_flow.py
+++ b/homeassistant/components/reolink/config_flow.py
@@ -8,6 +8,7 @@
 from typing import Any
 
 from reolink_aio.api import ALLOWED_SPECIAL_CHARS
+from reolink_aio.baichuan import DEFAULT_BC_PORT
 from reolink_aio.exceptions import (
     ApiError,
     CredentialsInvalidError,
@@ -37,7 +38,7 @@
 from homeassistant.helpers.device_registry import format_mac
 from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
 
-from .const import CONF_SUPPORTS_PRIVACY_MODE, CONF_USE_HTTPS, DOMAIN
+from .const import CONF_BC_PORT, CONF_SUPPORTS_PRIVACY_MODE, CONF_USE_HTTPS, DOMAIN
 from .exceptions import (
     PasswordIncompatible,
     ReolinkException,
@@ -287,6 +288,7 @@ async def async_step_user(
             if not errors:
                 user_input[CONF_PORT] = host.api.port
                 user_input[CONF_USE_HTTPS] = host.api.use_https
+                user_input[CONF_BC_PORT] = host.api.baichuan.port
                 user_input[CONF_SUPPORTS_PRIVACY_MODE] = host.api.supported(
                     None, "privacy_mode"
                 )
@@ -328,6 +330,9 @@ async def async_step_user(
                 {
                     vol.Optional(CONF_PORT): cv.positive_int,
                     vol.Required(CONF_USE_HTTPS, default=False): bool,
+                    vol.Required(
+                        CONF_BC_PORT, default=DEFAULT_BC_PORT
+                    ): cv.positive_int,
                 }
             )
 
diff --git a/homeassistant/components/reolink/const.py b/homeassistant/components/reolink/const.py
index 7bd93337c4614..026d121988102 100644
--- a/homeassistant/components/reolink/const.py
+++ b/homeassistant/components/reolink/const.py
@@ -3,4 +3,5 @@
 DOMAIN = "reolink"
 
 CONF_USE_HTTPS = "use_https"
+CONF_BC_PORT = "baichuan_port"
 CONF_SUPPORTS_PRIVACY_MODE = "privacy_mode_supported"
diff --git a/homeassistant/components/reolink/host.py b/homeassistant/components/reolink/host.py
index 2f646ba909012..53061500e3278 100644
--- a/homeassistant/components/reolink/host.py
+++ b/homeassistant/components/reolink/host.py
@@ -12,6 +12,7 @@
 import aiohttp
 from aiohttp.web import Request
 from reolink_aio.api import ALLOWED_SPECIAL_CHARS, Host
+from reolink_aio.baichuan import DEFAULT_BC_PORT
 from reolink_aio.enums import SubType
 from reolink_aio.exceptions import NotSupportedError, ReolinkError, SubscriptionError
 
@@ -33,7 +34,7 @@
 from homeassistant.helpers.storage import Store
 from homeassistant.util.ssl import SSLCipherList
 
-from .const import CONF_SUPPORTS_PRIVACY_MODE, CONF_USE_HTTPS, DOMAIN
+from .const import CONF_BC_PORT, CONF_SUPPORTS_PRIVACY_MODE, CONF_USE_HTTPS, DOMAIN
 from .exceptions import (
     PasswordIncompatible,
     ReolinkSetupException,
@@ -91,6 +92,7 @@ def get_aiohttp_session() -> aiohttp.ClientSession:
             protocol=options[CONF_PROTOCOL],
             timeout=DEFAULT_TIMEOUT,
             aiohttp_get_session_callback=get_aiohttp_session,
+            bc_port=config.get(CONF_BC_PORT, DEFAULT_BC_PORT),
         )
 
         self.last_wake: float = 0
diff --git a/homeassistant/components/reolink/strings.json b/homeassistant/components/reolink/strings.json
index b72e7bbd00d8d..6e40cac76b45f 100644
--- a/homeassistant/components/reolink/strings.json
+++ b/homeassistant/components/reolink/strings.json
@@ -8,6 +8,7 @@
           "host": "[%key:common::config_flow::data::host%]",
           "port": "[%key:common::config_flow::data::port%]",
           "use_https": "Enable HTTPS",
+          "baichuan_port": "Basic Service Port",
           "username": "[%key:common::config_flow::data::username%]",
           "password": "[%key:common::config_flow::data::password%]"
         },
@@ -15,6 +16,7 @@
           "host": "The hostname or IP address of your Reolink device. For example: '192.168.1.25'.",
           "port": "The port to connect to the Reolink device. For HTTP normally: '80', for HTTPS normally '443'.",
           "use_https": "Use a HTTPS (SSL) connection to the Reolink device.",
+          "baichuan_port": "The 'Basic Service Port' to connect to the Reolink device. Normally '9000' unless manually changed in the Reolink desktop client.",
           "username": "Username to login to the Reolink device itself. Not the Reolink cloud account.",
           "password": "Password to login to the Reolink device itself. Not the Reolink cloud account."
         }
diff --git a/tests/components/reolink/conftest.py b/tests/components/reolink/conftest.py
index 2862aa55b4de5..5784a38ec9dc6 100644
--- a/tests/components/reolink/conftest.py
+++ b/tests/components/reolink/conftest.py
@@ -10,6 +10,7 @@
 
 from homeassistant.components.reolink.config_flow import DEFAULT_PROTOCOL
 from homeassistant.components.reolink.const import (
+    CONF_BC_PORT,
     CONF_SUPPORTS_PRIVACY_MODE,
     CONF_USE_HTTPS,
     DOMAIN,
@@ -48,6 +49,7 @@
 TEST_CAM_MODEL = "RLC-123"
 TEST_DUO_MODEL = "Reolink Duo PoE"
 TEST_PRIVACY = True
+TEST_BC_PORT = 5678
 
 
 @pytest.fixture
@@ -134,6 +136,7 @@ def reolink_connect_class() -> Generator[MagicMock]:
         # Baichuan
         host_mock.baichuan = create_autospec(Baichuan)
         # Disable tcp push by default for tests
+        host_mock.baichuan.port = TEST_BC_PORT
         host_mock.baichuan.events_active = False
         host_mock.baichuan.privacy_mode.return_value = False
         host_mock.baichuan.subscribe_events.side_effect = ReolinkError("Test error")
@@ -169,6 +172,7 @@ def config_entry(hass: HomeAssistant) -> MockConfigEntry:
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
             CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
diff --git a/tests/components/reolink/test_config_flow.py b/tests/components/reolink/test_config_flow.py
index 4fe671f8ccac9..e706af0d06762 100644
--- a/tests/components/reolink/test_config_flow.py
+++ b/tests/components/reolink/test_config_flow.py
@@ -19,6 +19,7 @@
 from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL
 from homeassistant.components.reolink.config_flow import DEFAULT_PROTOCOL
 from homeassistant.components.reolink.const import (
+    CONF_BC_PORT,
     CONF_SUPPORTS_PRIVACY_MODE,
     CONF_USE_HTTPS,
     DOMAIN,
@@ -40,6 +41,7 @@
 
 from .conftest import (
     DHCP_FORMATTED_MAC,
+    TEST_BC_PORT,
     TEST_HOST,
     TEST_HOST2,
     TEST_MAC,
@@ -88,6 +90,7 @@ async def test_config_flow_manual_success(
         CONF_PORT: TEST_PORT,
         CONF_USE_HTTPS: TEST_USE_HTTPS,
         CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
+        CONF_BC_PORT: TEST_BC_PORT,
     }
     assert result["options"] == {
         CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -140,6 +143,7 @@ async def test_config_flow_privacy_success(
         CONF_PORT: TEST_PORT,
         CONF_USE_HTTPS: TEST_USE_HTTPS,
         CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
+        CONF_BC_PORT: TEST_BC_PORT,
     }
     assert result["options"] == {
         CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -290,6 +294,7 @@ async def test_config_flow_errors(
             CONF_HOST: TEST_HOST,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
     )
 
@@ -302,6 +307,7 @@ async def test_config_flow_errors(
         CONF_PORT: TEST_PORT,
         CONF_USE_HTTPS: TEST_USE_HTTPS,
         CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
+        CONF_BC_PORT: TEST_BC_PORT,
     }
     assert result["options"] == {
         CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -322,6 +328,7 @@ async def test_options_flow(hass: HomeAssistant, mock_setup_entry: MagicMock) ->
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: "rtsp",
@@ -360,6 +367,7 @@ async def test_reauth(hass: HomeAssistant, mock_setup_entry: MagicMock) -> None:
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -405,6 +413,7 @@ async def test_reauth_abort_unique_id_mismatch(
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -474,6 +483,7 @@ async def test_dhcp_flow(hass: HomeAssistant, mock_setup_entry: MagicMock) -> No
         CONF_PORT: TEST_PORT,
         CONF_USE_HTTPS: TEST_USE_HTTPS,
         CONF_SUPPORTS_PRIVACY_MODE: TEST_PRIVACY,
+        CONF_BC_PORT: TEST_BC_PORT,
     }
     assert result["options"] == {
         CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -496,6 +506,7 @@ async def test_dhcp_ip_update_aborted_if_wrong_mac(
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -536,6 +547,7 @@ async def test_dhcp_ip_update_aborted_if_wrong_mac(
             protocol=DEFAULT_PROTOCOL,
             timeout=DEFAULT_TIMEOUT,
             aiohttp_get_session_callback=ANY,
+            bc_port=TEST_BC_PORT,
         )
         assert expected_call in reolink_connect_class.call_args_list
 
@@ -593,6 +605,7 @@ async def test_dhcp_ip_update(
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -635,6 +648,7 @@ async def test_dhcp_ip_update(
             protocol=DEFAULT_PROTOCOL,
             timeout=DEFAULT_TIMEOUT,
             aiohttp_get_session_callback=ANY,
+            bc_port=TEST_BC_PORT,
         )
         assert expected_call in reolink_connect_class.call_args_list
 
@@ -671,6 +685,7 @@ async def test_dhcp_ip_update_ingnored_if_still_connected(
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -702,6 +717,7 @@ async def test_dhcp_ip_update_ingnored_if_still_connected(
         protocol=DEFAULT_PROTOCOL,
         timeout=DEFAULT_TIMEOUT,
         aiohttp_get_session_callback=ANY,
+        bc_port=TEST_BC_PORT,
     )
     assert expected_call in reolink_connect_class.call_args_list
 
@@ -731,6 +747,7 @@ async def test_reconfig(hass: HomeAssistant, mock_setup_entry: MagicMock) -> Non
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
@@ -777,6 +794,7 @@ async def test_reconfig_abort_unique_id_mismatch(
             CONF_PASSWORD: TEST_PASSWORD,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,
diff --git a/tests/components/reolink/test_init.py b/tests/components/reolink/test_init.py
index 28d8c542f4f3a..ad7f5540b0413 100644
--- a/tests/components/reolink/test_init.py
+++ b/tests/components/reolink/test_init.py
@@ -19,7 +19,7 @@
     FIRMWARE_UPDATE_INTERVAL,
     NUM_CRED_ERRORS,
 )
-from homeassistant.components.reolink.const import DOMAIN
+from homeassistant.components.reolink.const import CONF_BC_PORT, DOMAIN
 from homeassistant.config_entries import ConfigEntryState
 from homeassistant.const import (
     CONF_PORT,
@@ -38,6 +38,7 @@
 from homeassistant.setup import async_setup_component
 
 from .conftest import (
+    TEST_BC_PORT,
     TEST_CAM_MODEL,
     TEST_HOST_MODEL,
     TEST_MAC,
@@ -762,6 +763,21 @@ async def test_port_changed(
     assert config_entry.data[CONF_PORT] == 4567
 
 
+async def test_baichuan_port_changed(
+    hass: HomeAssistant,
+    reolink_connect: MagicMock,
+    config_entry: MockConfigEntry,
+) -> None:
+    """Test config_entry baichuan port update when it has changed during initial login."""
+    assert config_entry.data[CONF_BC_PORT] == TEST_BC_PORT
+    reolink_connect.baichuan.port = 8901
+
+    assert await hass.config_entries.async_setup(config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert config_entry.data[CONF_BC_PORT] == 8901
+
+
 async def test_privacy_mode_on(
     hass: HomeAssistant,
     freezer: FrozenDateTimeFactory,
diff --git a/tests/components/reolink/test_media_source.py b/tests/components/reolink/test_media_source.py
index 9c5be08e9b6b7..66d61d591b772 100644
--- a/tests/components/reolink/test_media_source.py
+++ b/tests/components/reolink/test_media_source.py
@@ -15,7 +15,7 @@
     async_resolve_media,
 )
 from homeassistant.components.reolink.config_flow import DEFAULT_PROTOCOL
-from homeassistant.components.reolink.const import CONF_USE_HTTPS, DOMAIN
+from homeassistant.components.reolink.const import CONF_BC_PORT, CONF_USE_HTTPS, DOMAIN
 from homeassistant.components.stream import DOMAIN as MEDIA_STREAM_DOMAIN
 from homeassistant.const import (
     CONF_HOST,
@@ -31,6 +31,7 @@
 from homeassistant.setup import async_setup_component
 
 from .conftest import (
+    TEST_BC_PORT,
     TEST_HOST2,
     TEST_HOST_MODEL,
     TEST_MAC2,
@@ -337,6 +338,7 @@ async def test_browsing_not_loaded(
             CONF_PASSWORD: TEST_PASSWORD2,
             CONF_PORT: TEST_PORT,
             CONF_USE_HTTPS: TEST_USE_HTTPS,
+            CONF_BC_PORT: TEST_BC_PORT,
         },
         options={
             CONF_PROTOCOL: DEFAULT_PROTOCOL,


Code review comments:
diff_hunk: @@ -8,13 +8,15 @@
           "host": "[%key:common::config_flow::data::host%]",
           "port": "[%key:common::config_flow::data::port%]",
           "use_https": "Enable HTTPS",
+          "baichuan_port": "Basic Service Port",
path: homeassistant/components/reolink/strings.json
text: @user1:
```suggestion
          "baichuan_port": "Basic service port",
```

diff_hunk: @@ -328,6 +330,9 @@ async def async_step_user(
                 {
                     vol.Optional(CONF_PORT): cv.positive_int,
                     vol.Required(CONF_USE_HTTPS, default=False): bool,
+                    vol.Required(
+                        CONF_BC_PORT, default=DEFAULT_BC_PORT
+                    ): cv.positive_int,
path: homeassistant/components/reolink/config_flow.py
text: @user1:
Why aren't you using `cv.port`?

@author:
Because I did not know cv.port existed, changed it now.
Thanks!