Skip to content

Modbus bit_switch and bit_sensor implementation#47240

Closed
yury-sannikov wants to merge 1 commit intohome-assistant:devfrom
yury-sannikov:jan_based_bin_sensor
Closed

Modbus bit_switch and bit_sensor implementation#47240
yury-sannikov wants to merge 1 commit intohome-assistant:devfrom
yury-sannikov:jan_based_bin_sensor

Conversation

@yury-sannikov
Copy link
Copy Markdown
Contributor

@yury-sannikov yury-sannikov commented Mar 1, 2021

Breaking change

This PR adds new functionality and should not introduce any breaking changes.
❗ This PR is based on the @janiversen PR #46591

Proposed change

bit_switch allows mapping Modbus holding register bits to a HomeAssistant Switch. All switch operations such as on, off, and toggle works against individual bits of a Modbus holding register. You may use the Modbus input register as well, however, a switch will remain read-only.

bit_sensor is pretty similar to the bit_switch. However, with the bit_sensor you are not limited to 16bit Modbus register length. You may specify any arbitrary count and specify the bit number of the interest.

Since multiple sensors or switches can read from the same Modbus register, a functools.lru_cache caching was added to cache Modbus register for one second. This should be enough to avoid re-reads. Any write operation to any of the registers reset the cache.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Example entry for configuration.yaml:

# Example configuration.yaml
modbus:
  - name: powermain
    type: tcp
    host: 192.168.5.222
    port: 2020
    sensors:
      - name: pwrmain_input_status
        slave: 16
        address: 512
        input_type: holding
    bit_switches:
      - name: pwrmain_shut_off_secondary_ups_load
        slave: 16
        address: 516
        command_bit_number: 0
      - name: pwrmain_airconditioneer_feed_from_solar_invertor
        slave: 16
        address: 516
        command_bit_number: 1
    bit_sensors:
      - name: pwrmain_main_feed_under_over_voltage
        slave: 16
        address: 514
        bit_number: 1
      - name: pwrmain_controller_fault_watchdog_took_over
        slave: 16
        address: 514
        bit_number: 2
      - name: pwrmain_main_feed_phase_order_error
        slave: 16
        address: 514
        bit_number: 6
      - name: pwrmain_watchdog_failure
        slave: 16
        address: 514
        bit_number: 7
      - name: pwrmain_emergency_feed_outage
        slave: 16
        address: 514
        bit_number: 10
      - name: pwrmain_reserve_feed_outage
        slave: 16
        address: 514
        bit_number: 11

Additional information

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • The code has been formatted using Black (black --fast homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • Untested files have been added to .coveragerc.

The integration reached or maintains the following Integration Quality Scale:

  • No score or internal
  • 🥈 Silver
  • 🥇 Gold
  • 🏆 Platinum

To help with the load of incoming pull requests:

@probot-home-assistant
Copy link
Copy Markdown

Hey there @adamchengtkc, @janiversen, @vzahradnik, mind taking a look at this pull request as its been labeled with an integration (modbus) you are listed as a codeowner for? Thanks!
(message by CodeOwnersMention)

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

cc @nagyrobi

@janiversen
Copy link
Copy Markdown
Member

Look good from a first view, I will go into detail a bit later

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Cool, then I will start working on the documentation PR. lmk if you feel we can use better names

@janiversen
Copy link
Copy Markdown
Member

Remark I have an open PR for documentation as well, yours should extend my PR.

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

yury-sannikov commented Mar 3, 2021

Documentation PR has been added based on the home-assistant/home-assistant.io#16678 PR

@janiversen
Copy link
Copy Markdown
Member

Hi I wanted to do a proper review, but for some reason I cannot exclude my PR so I review only your changes. As soon as my PR is merged (just waiting for a maintainer to do it), then I swiftly review your PR.

I have downloaded it too, and it works on my Mac.

Copy link
Copy Markdown
Member

@janiversen janiversen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your changes in sensor/switch needs some more thinking. I like the idea of a base class.



@lru_cache(maxsize=32)
def _read_cached(client, what, unit, address, count, ttl_bucket):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really do not see the need for this added complexity, it does not seem to make your bit implementation easier, and it complicates the rest. I suggest to remove the lru_cache part (unless you have some good arguments, why not).

Copy link
Copy Markdown
Contributor Author

@yury-sannikov yury-sannikov Mar 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I have a configuration where each sensor/switch has the same address (in the case below it's 514) I have N read calls of the same Modbus register to the Modbus Client. If I have 16 sensors attached to the same address, the integration will end up with 16 reads of the same value through the Modbus.

This read cache holds the first read value for one second. This helps avoid making sequential calls. We can remove the cache, however, then we will be making the same read calls multiple times. This is not a big deal, however, may slow down updates.

The cache protects from multiple reads only during one update cycle and should be empty on the next cycle, which ensures by the 1 second expiration time.

    bit_sensors:
      - name: pwrmain_main_feed_under_over_voltage
        slave: 16
        address: 514
        bit_number: 1
      - name: pwrmain_controller_fault_watchdog_took_over
        slave: 16
        address: 514
        bit_number: 2
      - name: pwrmain_main_feed_phase_order_error
        slave: 16
        address: 514
        bit_number: 6

Since Modbus device is a black box, we can't assume that the cache will be valid for a longer period of time. That's why I picked one second.
Also, any write to the Modbus invalidates the cache. This might be unnecessary though because the next read cycle will start not earlier than a second later and the cache won't hit anyway.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your use case is a bit different than the normal ones, normally you would just read the 16bits in one go, and then split them. What I do not like here is that you force the cache on all read, and I am not so sure it is a good idea.

What you could easily do is make a bit_sensor.py, and in that file have special handling for the update call. E.g. you group the sensors (according to the bit pattern), the update call to the first sensor reads physically and either stores the value for the other sensors or updates all sensors. Update call to all other sensors are dummies.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Let me think about how to do that. I will revert _read_cached changes

Comment thread homeassistant/components/modbus/modbus.py
Comment thread homeassistant/components/modbus/modbus.py
Comment thread homeassistant/components/modbus/modbus.py Outdated
Comment thread homeassistant/components/modbus/modbus.py Outdated
Comment thread homeassistant/components/modbus/modbus.py Outdated
Comment thread homeassistant/components/modbus/modbus.py Outdated
Comment thread tests/components/modbus/conftest.py Outdated
read_result = (
ReadResult(register_words)
if register_words
else ModbusException("Modbus error")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are testing the test setup ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, I just want to emulate a disconnected state, when the read_* call throws an exception. I found a bug in my code and decided to add a test for that as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh that is a good idea, I like that.

Copy link
Copy Markdown
Member

@janiversen janiversen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second part of review. I have downloaded your PR, to isolate your changes, making it a bit harder to review, but I wanted to give you some feedback.

Comment thread tests/components/modbus/conftest.py Outdated
), mock.patch(
"homeassistant.components.modbus.modbus.ModbusUdpClient", return_value=mock_sync
), mock.patch(
"homeassistant.components.modbus.modbus._read_cached", return_value=read_result
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But your cache is in our source, I am patching pymodbus, because I want to test the code we have in the read functions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, not sure why you need to patch pymodbus to make it work. read_cached basically wraps all read* calls, that's why I removed the code with the ReadResult below.

Comment thread tests/components/modbus/conftest.py
Comment thread tests/components/modbus/conftest.py Outdated
check_config_only=False,
config_modbus=None,
scan_interval=None,
**kvargs,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you have added test for write, but it should be done properly, with named parameters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, make sense

Comment thread tests/components/modbus/conftest.py
Comment thread tests/components/modbus/conftest.py Outdated
entity_id = f"{entity_domain}.{device_name}"

# Call an arbitrary service
if "call_service" in kvargs:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you are allowing both a read and a write test in the same call, the idea of this function is one call pr test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this test design might be confusing. Let me rethink it based on the cache decision

Comment thread tests/components/modbus/conftest.py Outdated
# check write_register call
if "write_register" in kvargs:
args, kvargs = kvargs["write_register"]
mock_sync.write_register.assert_called_once_with(*args, **kvargs)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check that the values sent to pymodbus (bytes) correspond the to the parameters in the original call (which could be e.g. STATE_ON).

Comment thread tests/components/modbus/test_modbus_bit_switch.py Outdated
STATE_OFF,
SERVICE_TURN_ON,
# 0x40 yields OFF state due to the status bit 5
# however, SERVICE_TURN_ON enables bit 6 which make bit untouched
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice comment !!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, except 0x40 and 0x41, lol

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

@janiversen thank you for the review. I will make changes a bit later this week. The main takeaways are:

  • remove _read_cached in favor of some sort of read manager handling the sequential reads
  • keep non bit_sensor/bit_switch read logic as-is
  • refactor unit tests to roll back _read_cached changes
  • refactor unit tests to split state check, service call, and asserts on the Modbus write_ calls.
  • create separate bit_sensor/bit_switch.py files
    lmk if I missed anything.

@janiversen
Copy link
Copy Markdown
Member

janiversen commented Mar 10, 2021

I think those are the big thing, once that is done I will look at it all again

Your idea of having a base sensor class is very good, please keep that.

@yury-sannikov yury-sannikov force-pushed the jan_based_bin_sensor branch 2 times, most recently from d84828a to e0eb8d0 Compare March 10, 2021 18:38
@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Hi @janiversen. I think I addressed all the issues except extracting bit_sensor and bit_switch into their own files. That will require extracting the base classes into their own separate files which may increase the complexity of understanding the code.
Also, I separated the read cache from the others. Only bit sensor/switch uses it right now.

@janiversen
Copy link
Copy Markdown
Member

ok, I will take a look, but it might be a couple of days until I get some free time

@janiversen
Copy link
Copy Markdown
Member

The modbus integration have changed on dev, so you need to do:

  1. git pull on the dev branch
  2. git rebase dev on your pr branch
  3. git push -f on your pr branch
    once done I will do a review

)
except ConnectionException:
self._available = False
self.schedule_update_ha_state()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w/o this, the state would be STATE_UNKNOWN instead of STATE_UNAVAILABLE. Added tests as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be aware your PR is about bit_sensor, so please only add changes to that respect.

):
"""Initialize the modbus bit sensor."""
super().__init__(
ModbusReadCache(hub),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only bit switch/sensor uses read cache as a wrapper. Regular sensors/switches would not use read cache.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Before you start doing changes, please make sure your branch is rebased against dev, so that this PR only contain your changes (also no merge commits), in order to facilitate review.

Regarding the cache, we are currently thinking about a different more general way. You instanciate an object of each bit_sensor configured, but an alternative it is modbus.py to have 2 classes one with the real read and one with a virtual read (reusing the first read). That way you would instanciate the first bit_sensor with the real class (reading modbus) and the following with the second class. The reason we are thinking of this, is that we often see a configuration that read address, +1, +2 ..... and that like your bit_sensor is overload.

Copy link
Copy Markdown
Member

@janiversen janiversen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a ton of good code, but there are 2 general issues:

  • it seems (maybe wrongly) that you have moved code around, that is tricky
  • a number of your changes are not related to bit_sensor/bit_switch
    Avoiding those 2 would make review a lot easier and you would have approval a lot faster.

vol.Optional(CONF_STATE_OFF): cv.positive_int,
vol.Optional(CONF_STATE_ON): cv.positive_int,
vol.Optional(CONF_VERIFY_REGISTER): cv.positive_int,
vol.Optional(CONF_VERIFY_STATE, default=True): cv.boolean,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you adding this, is that needed for your bit_sensor implementation ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I will remove it

@@ -0,0 +1,26 @@
"""Read cache wrapper for the Modbus Hub methods."""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something local to bit_sensor / bit_switch so at least the file name is wrong. I would prefer you put this content in bit_sensor, and imported it in bit_switch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

CALL_TYPE_REGISTER_HOLDING,
CALL_TYPE_REGISTER_INPUT,
CONF_BIT_NUMBER,
CONF_BIT_SENSORS,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to belong in bit_sensor.py

config = None

for entry in discovery_info[CONF_SENSORS]:
for entry in discovery_info.get(CONF_BIT_SENSORS, []):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not mix sensor with bit_sensor !

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


class ModbusRegisterSensor(RestoreEntity, SensorEntity):
"""Modbus register sensor."""
class ModbusSensorBase(RestoreEntity, SensorEntity):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that we should not have 2 base on for register and one for coil ?

Comment thread tests/components/modbus/test_modbus.py Outdated
sensor_name = "test_sensor"
config_sensor = {CONF_NAME: sensor_name, CONF_ADDRESS: 51, CONF_BIT_NUMBER: 5}
if do_options:
config_sensor.update(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see in the other test files. if/else in the test cases are to be avoided

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is old standard, as I noted somewhere else...when you present new code, maintainers look with the present standards. I was asked to remove specifically the “if do_options” in my last PR (and it do make sense). This is a good example why not to move old code.

When I get time, I will change all tests to follow this new pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, cool. Sorry, that's my first PR to the HA, I might not know all the rules

array_type = None
device_config[CONF_ADDRESS] = 1234

if do_options:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Comment thread tests/components/modbus/test_modbus_bit_switch.py Outdated
@@ -0,0 +1,91 @@
"""Test modbus read cache."""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be part of the bit_sensor / bit_switch test, we do not want to caching generally.

Comment thread tests/components/modbus/test_modbus.py Outdated
@nagyrobi
Copy link
Copy Markdown
Contributor

nagyrobi commented Apr 1, 2021

Will this affect the coils as switches in any aspect?
Any improvement can be done perhaps to read the states in one go?
As far as I can see, on my 24-relay card, each coil state is being read one by one. Whilst with the standard, all of them could be read in one go.

@janiversen
Copy link
Copy Markdown
Member

@nagyrobi sounds likeyour problem is more what I described in issue #48497 which are in development

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

hi @janiversen
I created a pretty simple PR out of the dev branch which shows the issue I have with the bit_sensor. My goal is to make tests to work against the new bit_sensor. What I see is that async_setup_platform is never called.
Could you please take a quick look at the https://github.com/yury-sannikov/core/pull/2/files PR. You might spot the issue I'm fighting for a long time. Thank you!

@janiversen
Copy link
Copy Markdown
Member

you are basically missing to update modbus.py (the loop that calls all platforms under modbus). I have added some review comments.

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Thanks for pointing out it. I added suggested changes to the test PR and I'm getting the error I had before:

ERROR:homeassistant.setup:Setup failed for bit_sensor: Integration not found.

This happens when I add (CONF_BIT_SENSOR, CONF_BIT_SENSORS), to the list for the load_platform

If I do (CONF_BINARY_SENSOR, CONF_BIT_SENSORS) instead, binary_sendor integration initialized instead of bit_sensor.

That's why I think there are only 2 ways to hook into:

  • add bit_sensor code into binary_senor async_setup_platform fn
  • set up a bit_sensor integration (which I feel is an overkill)

I'm looking for the third approach, which I'm struggling to find out.

@janiversen
Copy link
Copy Markdown
Member

have a look at pr #48558 a fan is also no standard.

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Any suggestions on how can I go ahead with this PR for the non-standard domain (senor and switch)?

@janiversen
Copy link
Copy Markdown
Member

For a non-standard domain you need to ask in the developer forum or use discord. But I still do not see why you continue trying to make a non-standard domain.

What you try to do looks like a specialised version of binary_sensor and switch (as I have written earlier). If you cannot trix HA to call you directly (load_component) then 1 single "if" in setup() of binary_sensor.py and switch.py is all that are needed to call you setup and do what you like (well and of course import a function from your file(s)), no need to make things more complicated.

One of the problems I have had reviewing your PR is all your changes in the existing code, causing me to wonder why they are need in order to add a specialised binary_sensor/switch. A clear implementation without all the other stuff is easier to review and easier to guide you on.

Apart from that, your changes in existing code are in conflict with dev, please rebase without merge.

@janiversen
Copy link
Copy Markdown
Member

Specific example:

from <yourfile> import <your setup func>

def async_setup_platform(
...
   for entry in ......
       if <your_key> in entry
           <your setup func>
          return

you can do it this simple, and without touching other parts of the existing code.

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Oh, cool, that makes sense now. I thought I'm not allowed to do any changes in the binary_sensor.py and switch.py. If I can hook into it like #47240 (comment) I think I'm good now. Thank you!

@yury-sannikov yury-sannikov force-pushed the jan_based_bin_sensor branch from 7026a65 to 9952081 Compare April 9, 2021 08:51
@janiversen
Copy link
Copy Markdown
Member

are you aware that you have changes in 12 files? that sound like overkill for what you want to implement.

may I suggest you do “git rebase” and squash the commit so you have only a few where you easier check which files are changed.

When your PR get merged the maintainer will squash all commits into one, but keep the commit message from every commit.

@yury-sannikov yury-sannikov force-pushed the jan_based_bin_sensor branch from 43d23c3 to e95cb0b Compare April 9, 2021 10:45
@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Squashed into one commit. I would appreciate it if you can guide me on how can I reduce the number of changed files. I might be missing something.

@janiversen
Copy link
Copy Markdown
Member

If you say it is all needed changes, then you cannot reduce the number and at least you have removed 2 files. It looks to as if your changes to conftest.py does not really matter.

I see you are still using "sensor" as base, but as I understand it bit_sensor can return true/false, that is the definition of binary_sensor. I wonder if you will get problems with Lovelace when forcing a sensor (not binary_sensor) to only be true/false.

I see you have introduced your own setup which is good, but I think your code have an unintended sideeffect. For a bit_sensor your setup is called to add the sensor, but then the sensor code continues with the same discovery_info, meaning it is added a second time. (this was just a quick look, I might have overlooked something).

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

I see you are still using "sensor" as base, but as I understand it bit_sensor can return true/false, that is the definition of binary_sensor. I wonder if you will get problems with Lovelace when forcing a sensor (not binary_sensor) to only be true/false.

Right, forgot about you mentioned it earlier. Let me fix it a bit later.

I see you have introduced your own setup which is good, but I think your code have an unintended sideeffect. For a bit_sensor your setup is called to add the sensor, but then the sensor code continues with the same discovery_info, meaning it is added a second time. (this was just a quick look, I might have overlooked something).

bit_sensor uses discovery_info.get(CONF_BIT_SENSORS, []) and sensor use discovery_info.get(CONF_SENSORS, []) Should be no dupes even if both setup_bit_sensors and the code after it adds the sensors

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Hi @janiversen
I believe I addressed all the issues. I checked the code against my hardware setup and it's working as expected. If you have some time, could you pls do another round of review?

@janiversen
Copy link
Copy Markdown
Member

2 general items:

  • please update the description the PR you refer is merged.
  • please update the doc. PR as frenck requested a while ago.

Copy link
Copy Markdown
Member

@janiversen janiversen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not looked too closely at your test cases, it seems to me you are redefining the test harness we use for other platforms, and that makes the code a lot harder to read.

config = None

for entry in discovery_info[CONF_BINARY_SENSORS]:
sensors.extend(setup_bit_sensors(hass, discovery_info))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs at very least a comment. Reading this code, it looks as if you first extend sensors with bit-sensors and the continue with the normal sensors.

for entry in discovery_info[CONF_BINARY_SENSORS]:
sensors.extend(setup_bit_sensors(hass, discovery_info))

for entry in discovery_info.get(CONF_BINARY_SENSORS, []):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change to info.get ? That is not a needed change.

import logging
import time

from pymodbus.exceptions import ConnectionException, ModbusException
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no need to check for ConnectionException it is also a ModbusException.

import time

from pymodbus.exceptions import ConnectionException, ModbusException
from pymodbus.pdu import ExceptionResponse
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only need to check for ModbusException. Which btw is something which are currently being removed in all platforms, I have a PR pending merge for that.

_LOGGER = logging.getLogger(__name__)


@lru_cache(maxsize=32)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you limit to 32, then you should also check that no more than 32 are used.

array_name_discovery,
array_name_old_config,
register_words,
register_words_or_exception,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not change the test methods in a PR where you implement new functionality. In case of problems it is quite hard to determine whether it is the new functionaliy or the test changes.

}

mock_sync = mock.MagicMock()
# Setup inputs for the sensor
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?? This is not just for a sensor.

register_words_or_exception
if isinstance(register_words_or_exception, Exception)
else ReadResult(register_words_or_exception)
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea, but we do not want to test pymodbus in every platform, that is the responsibiliy and thus testing of modbus.py.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a Modbus test, it's a negative case testing wish Modbus integration lack of, unfortuately

async_fire_time_changed(hass, now)
await hass.async_block_till_done()

# Check state
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this comment ?

),
],
)
@mock.patch.object(ModbusHub, "read_holding_registers")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a standard function in conftest you should use.

@yury-sannikov
Copy link
Copy Markdown
Contributor Author

Most of the review comments do not make much sense to me. Not sure I will ever be able to make it to conform to the controversial suggestions here. Feel free to use some portions of the code for further development of the platform. I will focus my energy on more meaningful things. Thanks for your time.

@github-actions github-actions Bot locked and limited conversation to collaborators Apr 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants