Modern Forms Smart Fans - Integration

Hmm, how is the Wifi connection to the fan? Maybe its struggling?
Another option is to limit the refresh interval. I believe the setting is per fan and defaults to 10s.
try something like

modernforms:
  - host: ...
    name: ...
    light: ...
    scan_interval: 20

Will try to increase scan interval and report back. Two of the fans are < 15 feet and line of sight from one of the Google Mesh units so signal strength should not be an issue.
Thanks!

Setting scan_interval produced this error:

Logger: homeassistant.setup
Source: helpers/event.py:459
First occurred: 12:21:20 PM (1 occurrences)
Last logged: 12:21:20 PM

Error during setup of component modernforms
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/setup.py", line 191, in _async_setup_component
    result = await task
  File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/modernforms/__init__.py", line 28, in setup
    hass.data[DOMAIN][DEVICES].append(ModernFormsDevice(hass, name, host, has_light, scan_interval))
  File "/config/custom_components/modernforms/__init__.py", line 64, in __init__
    self.poll = async_track_time_interval(hass, update_action, interval)
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 468, in async_track_time_interval
    remove = async_track_point_in_utc_time(hass, interval_listener, next_interval())
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 459, in next_interval
    return dt_util.utcnow() + interval
TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'int'

I will look at the code too to see if I can correct the incompatability b/t datetime and int variable types.
Paul

Edited modernforms component, file __init__.py as follows:
Added:

import datetime 

to line 3

Changed line ~66 from:

self.poll = async_track_time_interval(hass, update_action, interval)

to:

    self.poll = async_track_time_interval(hass, update_action, datetime.timedelta(0,interval))

This seemed to remove the type casting error with the interval variable. Will follow up with whether the increasing the interval helped.
Paul

1 Like

Thank you for this. Since there’s already a correct import at the top, what seems to have done the trick for me is just changing line 64:

    self.poll = async_track_time_interval(hass, update_action, timedelta(0, interval))

Since I’m relatively new to Home Assistant, I wonder what would be the best way to confirm this change had a desired effect (i.e. actually changed the polling interval).

Context: I have two Modern Forms Roboto fans, and they have been rather wonky since I started using this integration. After about a day or so, their TCP/IP stack appears to freeze solid, and they stop responding. I have a strong suspicion it’s the “intense” polling from Home Assistant integration that is triggering a memory leak of some sort on the fans. I want to see if slower polling delays the reintroduction of the issue, or it’s simply time-based.

@tscibilia

Thank you for the code. Please forgive me as I am really new to all this, I have my fan working with the standard cards but I really like the one you put together. Can you tell me where I need to actually put this code? I have my home assistant running on a Pi 3B. Thanks in advance for any assistance.

It is good to see I am not the only one with this problem.

And yup, did not look carefully and timedelta from datetime was already imported. Changing the singular line at ~64 was all that was needed.

I agree that a probable memory leak in the fan’s firmware seems like the most likely culprit. Slowing the polling will only eliminate this if the firmware has a garbage collection scheme. I wonder if we could command the fan to reboot in the dead of the night?

My fans have been more stable after decreasing the polling rate.

Can you tell me where I need to actually put this code?

Look here.

Sorry but this does not answer my question. I already have the modern forms custom component up and running and I have the fans working. I was asking @tscibilia because he seemed to have created a custom tile for the dashboard. That is the code that I need to determine the location for.

Well, there’s hope they have not re-invented the wheel, and used some platform that does garbage collection. 10 second polling for a ceiling fan is rather aggressive to be honest :-). I have set mine to 1-minute (even thinking 3-minutes would be just fine), and now I am observing to see if they lock up again.

Also, since Python isn’t my poison of choice, I realized we made a slight error in our fix. More appropriate would be to change line 64 to:

self.poll = async_track_time_interval(hass, update_action, timedelta(seconds=interval))

Simple use of fold-entity-row and slider-entity-row to show light and fan support:

image

Yeah… lowered polling interval did very little for my issue. One of the fans died during the day in its usual fashion, requiring power cycle.

Same here. It does seem like they take a bit longer to fail, but they do. I will try to increase polling to 90 seconds and see what happens. This might help diagnose the problem.

And thanks for the improvement on the code. Python is not my primary “poison” either.
Paul

1 Like

I modified tscibilia’s code, adding custom colors and removing the need for additional libraries. Take a look here if you have the modern forms fans: https://github.com/paulearley/custom-fan-card6/wiki

screenshot? :slight_smile:

2020-08-29_8-37-26 2020-08-28_20-52-22

Hi all, this integration was working for me for 1-2 weeks when all of a sudden it seems to have started to crash lovelace. Everything will be working great for 12 hours or so when I’ll see the following error in my log file and lovelace will stop responding. Restarting Home Assistance will clear the problem for another 12 hours or so.

After a restart the Fan Control works as expected. The fan has been assigned a static IP so I rule out the fan changing IP addresses.

Any suggestions on how to prevent a failure to connect to the fan from locking up lovelace?

2020-09-11 03:55:21 ERROR (MainThread) [homeassistant] Error doing job: Future exception was never retrieved
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 159, in _new_conn
    conn = connection.create_connection(
  File "/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py", line 84, in create_connection
    raise err
  File "/usr/local/lib/python3.8/site-packages/urllib3/util/connection.py", line 74, in create_connection
    sock.connect(sa)
TimeoutError: [Errno 110] Operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 670, in urlopen
    httplib_response = self._make_request(
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 392, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/local/lib/python3.8/http/client.py", line 1240, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/local/lib/python3.8/http/client.py", line 1286, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/local/lib/python3.8/http/client.py", line 1235, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/local/lib/python3.8/http/client.py", line 1006, in _send_output
    self.send(msg)
  File "/usr/local/lib/python3.8/http/client.py", line 946, in send
    self.connect()
  File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 187, in connect
    conn = self._new_conn()
  File "/usr/local/lib/python3.8/site-packages/urllib3/connection.py", line 171, in _new_conn
    raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7fc1879177c0>: Failed to establish a new connection: [Errno 110] Operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/requests/adapters.py", line 439, in send
    resp = conn.urlopen(
  File "/usr/local/lib/python3.8/site-packages/urllib3/connectionpool.py", line 726, in urlopen
    retries = retries.increment(
  File "/usr/local/lib/python3.8/site-packages/urllib3/util/retry.py", line 439, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='192.168.1.40', port=80): Max retries exceeded with url: /mf (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fc1879177c0>: Failed to establish a new connection: [Errno 110] Operation timed out'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/modernforms/__init__.py", line 61, in update_action
    self.update_status()
  File "/config/custom_components/modernforms/__init__.py", line 121, in update_status
    self._send_request({"queryDynamicShadowData":1})
  File "/config/custom_components/modernforms/__init__.py", line 124, in _send_request
    r = requests.post(self.url, json=data)
  File "/usr/local/lib/python3.8/site-packages/requests/api.py", line 119, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 530, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 643, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='192.168.1.40', port=80): Max retries exceeded with url: /mf (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fc1879177c0>: Failed to establish a new connection: [Errno 110] Operation timed out'))

Hmm looks like exceptions might be leaking and might need to be handled. I’ve never had this issue probably because i have my HA instance restart at 3 am every night.

This is the exact same error cascade I have and icemarkom has. He and I were able to reproduce similar failures on a simple go app that queried the fan at a regular interval to test the hypothesis that the tcp/ip stack fails due to too frequent queries (most likely due to inadequate garbage collection by the Modern Forms controller). We were able to reproduce but the next step of bringing the errors to tech support has not happened.

Have you had your wall control fail? Our do, pulling the airgap allows the wall controller to reconnect with the fan.

Jim - I have not tried rebooting HA nightly. Have you never seen these errors?
Paul Earley

As Paul said, we have been testing our hypothesis for about a week or so now using a simple prober I wrote, and we can relatively reliably expect our fans to lock up.

I am not sure it’s the HA that is the problem here, but I have to confess, I have not tried running only the prober, without the HA integration being active. That may be an interesting experiment.

But… while I do not have a solution for our problem, there MAY be a workaround.

I noticed in the MF app there is an option to reboot the fan. I believe rebooting a fan daily may mask the underlying issue, combined with a less aggressive polling from HA (mine is set to 2 minutes, and my fans lock up about once every two days without my prober running).

Jim - since you did the heavy lifting for figuring out the REST API, save me the time for having to do packet captures - do you happen to know what the reboot command in the API is? I’d love to try out this little workaround.