Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to save configuration with protocol V1 & V2 #209

Closed
keyluke opened this issue Jul 7, 2024 · 12 comments · Fixed by #210
Closed

Unable to save configuration with protocol V1 & V2 #209

keyluke opened this issue Jul 7, 2024 · 12 comments · Fixed by #210
Labels
bug Something isn't working

Comments

@keyluke
Copy link

keyluke commented Jul 7, 2024

HA core version

2024.7.1

Integration version

0.4.3

Device type and model

Ariston DEOS 16s

Used App

MSmartHome

The description of problem

Dear all,
when trying to save the manual configuration for appliance, it cannot save it. It seems that a temporary file is saved at root location before saving the effective configuration file. The device authentication phase is successful. I provide the log of the issue (debug log enabled)

The logs

2024-07-07 17:21:33.032 DEBUG (MainThread) [midealocal.device] [31885837206704] Connecting to 192.168.1.240:6444
2024-07-07 17:21:33.038 DEBUG (MainThread) [midealocal.device] [31885837206704] Connected
2024-07-07 17:21:33.038 DEBUG (MainThread) [midealocal.device] [31885837206704] Authentication success
2024-07-07 17:21:33.038 DEBUG (MainThread) [midealocal.device] [31885837206704] Status update: {'available': True}
2024-07-07 17:21:33.040 ERROR (MainThread) [homeassistant.util.file] Saving file failed: 31885837206704.json
Traceback (most recent call last):
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/util/file.py", line 56, in write_utf8_file
with tempfile.NamedTemporaryFile(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 582, in NamedTemporaryFile
file = _io.open(dir, mode, buffering=buffering,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 579, in opener
fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 256, in _mkstemp_inner
fd = _os.open(file, flags, 0o600)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/tmpt1mx85_k'
2024-07-07 17:21:33.041 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/util/file.py", line 56, in write_utf8_file
with tempfile.NamedTemporaryFile(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 582, in NamedTemporaryFile
file = _io.open(dir, mode, buffering=buffering,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 579, in opener
fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 256, in _mkstemp_inner
fd = _os.open(file, flags, 0o600)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/tmpt1mx85_k'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/srv/homeassistant/lib/python3.12/site-packages/aiohttp/web_protocol.py", line 452, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/aiohttp/web_app.py", line 543, in _handle
resp = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/aiohttp/web_middlewares.py", line 114, in impl
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/security_filter.py", line 92, in security_filter_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/forwarded.py", line 210, in forwarded_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/request_context.py", line 26, in request_context_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/ban.py", line 85, in ban_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/auth.py", line 242, in auth_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/headers.py", line 32, in headers_middleware
response = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/helpers/http.py", line 73, in handle
result = await handler(request, **request.match_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/decorators.py", line 81, in with_admin
return await func(self, request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/config/config_entries.py", line 222, in post
return await super().post(request, flow_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/components/http/data_validator.py", line 74, in wrapper
return await method(view, request, data, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/helpers/data_entry_flow.py", line 122, in post
result = await self._flow_mgr.async_configure(flow_id, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/data_entry_flow.py", line 368, in async_configure
result = await self._async_configure(flow_id, user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/data_entry_flow.py", line 415, in _async_configure
result = await self._async_handle_step(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/data_entry_flow.py", line 518, in _async_handle_step
result: _FlowResultT = await getattr(flow, method)(user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/homeassistant/.homeassistant/custom_components/midea_ac_lan/config_flow.py", line 467, in async_step_manually
# save device json config when adding new device
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/homeassistant/.homeassistant/custom_components/midea_ac_lan/config_flow.py", line 126, in _save_device_config
record_file = storage_path.joinpath(f"{data[CONF_DEVICE_ID]!s}.json")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/helpers/json.py", line 198, in save_json
method(filename, json_data, private, mode=mode)
File "/srv/homeassistant/lib/python3.12/site-packages/homeassistant/util/file.py", line 66, in write_utf8_file
raise WriteError(error) from error
homeassistant.util.file.WriteError: [Errno 13] Permission denied: '/tmpt1mx85_k'

@keyluke keyluke added the bug Something isn't working label Jul 7, 2024
@wuwentao
Copy link
Owner

wuwentao commented Jul 8, 2024

@keyluke

I'm not understand why you got PermissionError: [Errno 13] Permission denied: '/tmpt1mx85_k'
seems it's your HA or OS permission changes by your? it's not like a normal result, why not your temp directory path go to root dir /? it should be /tmp/ ?
in addition, we will save it to HA /config/.storage/ dir, so please double check with your HA installation, whether you did some addition permission changes or specially dir setting and caused this Permission denied error.

one more question:

  1. you directly add with manually option or the first discovery and login/add device step?
  2. if your device using v3 protocol, please don't manual change it to v2, as it will caused error config and error control protocol, you can't use it anymore, there is too many error add device, we will disable and check it in next version.

@keyluke
Copy link
Author

keyluke commented Jul 8, 2024

@wuwentao
Thanks for your attention!
I also found it really strange. Maybe a homeassistant bug? I did not edit any permissions or default settings. I will investigate further to ensure proper settings in homeassistant anyway.
To answer you:

  1. I add with Configure manually inserting manually all the information required.
  2. Unfortunately my device does not support v3 protocol as I get Authentication failed. I can only get successful authentication with v2 protocol (that's why I moved to the Configure manually setting instead of Discover automatically

@keyluke
Copy link
Author

keyluke commented Jul 8, 2024

Little update: reverting back to v0.4.0 no problem in saving the configuration. Maybe an issue with storage_path = Path(self.hass.config.path(STORAGE_PATH)) in _save_device_config() ?

@wuwentao
Copy link
Owner

wuwentao commented Jul 8, 2024

could you answer previous question?
is there any specially changes in your storage path and operation steps?

it's only both different python path lib, and Path() should be the replace for old os.makedirs feature.
and it also works well in most of us HA env, so we should find out the difference if you can't pass with it.....

HA install ENV should be a important part from the error message.
we should got the detail and compare with old version.

@PandaDriver156
Copy link

I have a similar permission issue, logs below. The error only happens when using Linuxserver's version of Homeassistant, it does not happen with Homeassistant's official docker image.

2024-07-08 17:00:52.117 ERROR (MainThread) [homeassistant.util.file] Saving file failed: 153931628327762.json
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/homeassistant/util/file.py", line 56, in write_utf8_file
with tempfile.NamedTemporaryFile(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 582, in NamedTemporaryFile
file = _io.open(dir, mode, buffering=buffering,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 579, in opener
fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 256, in _mkstemp_inner
fd = _os.open(file, flags, 0o600)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/run/s6-rc:s6-rc-init:cnhBoP/servicedirs/svc-homeassistant/tmpxjh12ovs'
2024-07-08 17:00:52.120 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/homeassistant/util/file.py", line 56, in write_utf8_file
with tempfile.NamedTemporaryFile(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 582, in NamedTemporaryFile
file = _io.open(dir, mode, buffering=buffering,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 579, in opener
fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/tempfile.py", line 256, in _mkstemp_inner
fd = _os.open(file, flags, 0o600)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/run/s6-rc:s6-rc-init:cnhBoP/servicedirs/svc-homeassistant/tmpxjh12ovs'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/aiohttp/web_protocol.py", line 452, in _handle_request
resp = await request_handler(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/web_app.py", line 543, in _handle
resp = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/web_middlewares.py", line 114, in impl
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/security_filter.py", line 92, in security_filter_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/forwarded.py", line 210, in forwarded_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/request_context.py", line 26, in request_context_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/ban.py", line 85, in ban_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/auth.py", line 242, in auth_middleware
return await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/headers.py", line 32, in headers_middleware
response = await handler(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/helpers/http.py", line 73, in handle
result = await handler(request, **request.match_info)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/decorators.py", line 81, in with_admin
return await func(self, request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/config/config_entries.py", line 222, in post
return await super().post(request, flow_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/components/http/data_validator.py", line 74, in wrapper
return await method(view, request, data, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/helpers/data_entry_flow.py", line 122, in post
result = await self._flow_mgr.async_configure(flow_id, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/data_entry_flow.py", line 368, in async_configure
result = await self._async_configure(flow_id, user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/data_entry_flow.py", line 415, in _async_configure
result = await self._async_handle_step(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/homeassistant/data_entry_flow.py", line 518, in _async_handle_step
result: _FlowResultT = await getattr(flow, method)(user_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/midea_ac_lan/config_flow.py", line 467, in async_step_manually
self._save_device_config(data)
File "/config/custom_components/midea_ac_lan/config_flow.py", line 126, in _save_device_config
save_json(record_file.name, data)
File "/usr/local/lib/python3.12/site-packages/homeassistant/helpers/json.py", line 198, in save_json
method(filename, json_data, private, mode=mode)
File "/usr/local/lib/python3.12/site-packages/homeassistant/util/file.py", line 66, in write_utf8_file
raise WriteError(error) from error
homeassistant.util.file.WriteError: [Errno 13] Permission denied: '/run/s6-rc:s6-rc-init:cnhBoP/servicedirs/svc-homeassistant/tmpxjh12ovs'

@keyluke
Copy link
Author

keyluke commented Jul 8, 2024

could you answer previous question? is there any specially changes in your storage path and operation steps?

it's only both different python path lib, and Path() should be the replace for old os.makedirs feature. and it also works well in most of us HA env, so we should find out the difference if you can't pass with it.....

HA install ENV should be a important part from the error message. we should got the detail and compare with old version.

I did not edit any permissions on HA or my OS. Is it maybe due to the fact that I am using Homeassistant Core in a venv? The fact that HA tries to write to / instead to /tmp is maybe due to that empty string (looking at utils/file.py, a temporary file named tmprandomletters is created and then replaced by the actual filename:

    filename: str, utf8_data: bytes | str, private: bool = False, mode: str = "w"
) -> None:
    """Write a file and rename it into place.

    Writes all or nothing.
    """
    tmp_filename = ""
    encoding = "utf-8" if "b" not in mode else None
    try:
        # Modern versions of Python tempfile create this file with mode 0o600
        with tempfile.NamedTemporaryFile(
            mode=mode, encoding=encoding, dir=os.path.dirname(filename), delete=False
        ) as fdesc:
            fdesc.write(utf8_data)
            tmp_filename = fdesc.name
            if not private:
                os.fchmod(fdesc.fileno(), 0o644)
        os.replace(tmp_filename, filename)

@wuwentao Which kind of Homeassistant do you have? HA supervised, OS or Core? From @PandaDriver156 issue, the Linuxserver version uses also HA Core like me.

Edit: maybe found the bug. In:

    def _save_device_config(self, data: dict[str, Any]) -> None:
        """Save device config to json file with device id."""
        storage_path = Path(self.hass.config.path(STORAGE_PATH))
        storage_path.mkdir(parents=True, exist_ok=True)
        record_file = storage_path.joinpath(f"{data[CONF_DEVICE_ID]!s}.json")
        save_json(record_file.name, data)

passing record_file instead of record_file.name resolves the issue.
It will pass the whole path, and not only the filename.

@chemelli74
Copy link
Collaborator

passing record_file instead of record_file.name resolves the issue. It will pass the whole path, and not only the filename.

Thx a lot for your help.
I created a PR to fix it in the proper way ( so that mypy is happy as we are 😊 ).

@keyluke
Copy link
Author

keyluke commented Jul 9, 2024

Just saw that also in load_json f.name is used instead of f. May lead to the same issue as this one, just in case.

@wuwentao
Copy link
Owner

Just saw that also in load_json f.name is used instead of f. May lead to the same issue as this one, just in case.

thanks, just checked with it, it's not the same func, save_json using pathlib args, but this load_json f.name is from with xxx.open() as f, not pathlib func, it should be ok.

@keyluke
Copy link
Author

keyluke commented Jul 10, 2024

Just saw that also in load_json f.name is used instead of f. May lead to the same issue as this one, just in case.

thanks, just checked with it, it's not the same func, save_json using pathlib args, but this load_json f.name is from with xxx.open() as f, not pathlib func, it should be ok.

It seems to be an issue since an exception is thrown, but it is not if f is used instead of f.name (tested on my side). You can find the error in attachment.
midea.log

Edit: not a fatal error; the plugin loads correctly and works, but still an error while deleting and adding again a device for which the configuration has been already saved on .storage path.

@chemelli74
Copy link
Collaborator

This is a different error message and needs a new issues.

Repository owner locked and limited conversation to collaborators Jul 10, 2024
@wuwentao
Copy link
Owner

yes, from the error log, this is a event loop warning....

WARNING (MainThread) [homeassistant.util.loop] Detected blocking call to open with args ('/home/homeassistant/.homeassistant/.storage/midea_ac_lan/31885837206704.json',) inside the event loop

as it not support blocking call to open file, and it's a known issue, but may not impact feature with WARNING now, and still need to fix, but not a urgent issue, we may need to track it with new issue in future

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants