-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
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
Draft: Support wago 750 with enocean rebase2 #58916
Draft: Support wago 750 with enocean rebase2 #58916
Conversation
… them in the frontend
…with esp2 support
… (i.e. omnio rtf102, reference temperature)
…time_to_max_seconds
…two states at once
Hey there @bdurrer, mind taking a look at this pull request as it has been labeled with an integration ( |
Hey there @adamchengtkc, @janiversen, @vzahradnik, mind taking a look at this pull request as it has been labeled with an integration ( |
@MartinHjelmare In the modbus-enocean bridge the user could configure a local TCP port to expose and then give the user the possibility to choose between serial to tcp communicator in the config flow? |
Ok, but where would the bridge run? Would it be run by an integration? Which integration in that case? |
@MartinHjelmare Yes, the bridge would be run by a customer wago modbus integration whenever |
Ok. The enocean code owners will need to approve the addition of the TCP connection option to the enocean integration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The group concept implemented here, do not match what the modbus protocol offers, and seems limited to sensors only. Furthermore it (mis)uses the struct something we are in the process of phasing out, especially because is has a limit of 254 chars.
The correct concept for especially coils is to do 1 read the reads many coils and then update the corresponding binary_sensors.
you also need to add tests in order to not lower the coverage score, and to make proof of your concept.
@@ -90,6 +95,34 @@ def __init__(self, hub: ModbusHub, entry: dict[str, Any]) -> None: | |||
self._lazy_error_count = entry[CONF_LAZY_ERROR] | |||
self._lazy_errors = self._lazy_error_count | |||
|
|||
self._scan_group = entry.get(CONF_SCAN_GROUP) | |||
self._unique_id = ( | |||
f"modbus_{hub.name}_{self._slave}_{self._input_type}_{self._address}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you cannot use hub.name as part of the unique_id as it is something the user can change.
@@ -120,6 +154,11 @@ def async_hold(self, update: bool = True) -> None: | |||
self._attr_available = False | |||
self.async_write_ha_state() | |||
|
|||
@property | |||
def unique_id(self) -> str | None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be _attr
@@ -259,9 +325,14 @@ async def async_added_to_hass(self) -> None: | |||
|
|||
async def async_turn(self, command: int) -> None: | |||
"""Evaluate switch result.""" | |||
if self._call_active: | |||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems your code is based on an old version of the modbus integration, this code have been in modbus for quite a while.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remark my review is not complete, just to give you some items.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
I did a rebase before opening the PR
@janiversen The current implementation is up and running against a wago modbus controller (https://www.wago.com/global/d/1256). If they stated it right in th documentation, their implementation should be manufacturer independant. But this I can't test myself. I'm aware that tests are missing. This because at the moment I'm not able to run the tests against the modified |
I see you are doing it for sensor, but misusing the struct parameter. But e.g. for binary_sensor and switch I cannot see you do the grouping into 1 call. Furthermore when trying your config changes in a pure modbus environment I did not manage to create a group of switches. That might of course be because you did not update the documentation. The tests of modbus must be independent of the other integrations, so you could make them right away same goes for documentation. Your group concept have sone advantages, but at least makes the control of the configuration a lot more difficult. One concern is that it explodes the configuration, the current discussions move more in the direction of adding the group to a single sensor and generate slave sensors: As martin stated this PR is a mix of different integrations and that needs to be solved before you csn expect any indepth revviews |
max_address, | ||
scan_group, | ||
) | ||
update_listeners = self._update_listeners_by_scan_group[scan_group] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@janiversen Here we register update listeners based on a scan_group
and input_type
, regardless of the device itself. This has the advantage, that in the same request different devices can get updated. I created the concept of scan_group
to be able to query different type of inputs with a different interval. i.e. it makes sense to listen for wall switch changes at a shorter interval than for state changes of lights.
max_address, | ||
input_type, | ||
) | ||
result = await self.async_pymodbus_call( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@janiversen
Here we do the effectice modbus call, again independant of devices, just by input_type and per scan_group. Afterwards we update all registered devices with the starting_offset so the device knows where to start reading the results from it.
Why do you think listeners are expensive? Maybe it's just a wording issue, I meant listeners as normally used in java environments which means a reference to an object having a specific interface to be able to notify it. Is there a better way to inform a certain list of devices in pyton/HA?
@janiversen An example configuration would look like:
As already mentioned to Martin, the enocean bridge could be integrated differently using a local socket and a TCPCommunicator so I could in a wago specific integration bring up a local socket and transform the request manufacturer specifc to the modbus calls and vice-versa. |
Your example highlight some of the problems with scan_group:
others do (most normal):
so checking the validity of the addresses in a group and calculating how many words to read is relaying on the user configured everything correctly. Secondly I can make a group of sensors that contain 3 sensors INT32,STRING(3),FLOAT64, this will cause an alignment problem with struct. On top of that normally STRING are variable length (count is the maximum), so there is no way of knowing where FLOAT64 starts. These are some of the reason, why the group concept was replaced by a concept where you define a sensor/cover etc once with type, address etc, and then simply say how many you want. Pr default we then create “sensor” (master doing the actual read) and “sensor1” …. as slaves that are updated directly from the master. With that concept you are sure of the configuration. Independently if you submit a PR to add these changes to the modbus integration, or your own variant, the problems are identical and needs to be solved. |
Being able to poll faster than 1 second is probably an architecture discussion, since it potentially cause changes to enter core in millisecond range, which will affect e.g. the recorder. Forgot to mention your PR will not pass CI without tests, because modbus files are subject to 100% test coverage (controlled by coverage). |
@janiversen I did of course not change all the scan_intervals to milliseconds and kept all changes the way they work as before without using scan_groups if wanted. Just in the configuration of scan_groups I allow And for the testing: As sayed I'm aware of it. Would like to now in which direction i need to modify stuff to be compatible to your architectur and then would make one or multiple adjusted PR including tests. |
@janiversen Regarding the registers:
The sensor device itself it then responsible of decoding the values out of the response based on the provided offset_address inside the full resultset. But to be honest I don't read registers from my modbus system as there are no analog input or output devices connected. If I understand you correctly, the problem might be here:
Where I assume that we want to read one address per listener. If that's the problem I would suggest to add a register_count to the sensor configuration and add this count to the range of addresses to read from. This would of course then affect the address_offset provided to the next device, so this one would just start reading from a further register. Or did I miss something? |
Coils do have duplicates of address, because you always read a word, which is 16 coils !! Following your assumption about addressing you are bound to run into problems, between the first address of e.g. holding, INT32, and and max. address there might be other datatypes, so you cannot simple do “read_holding” address, count = (max - first). Typically modbus device mix the datatypes (and thereby the sizes), the do NOT have all INT32 in one address set. So your are suggesting to have “register_count” as well as “count”, that is going to very confusing to configure. And your explanations still do not help on the string case, where e.g. I define a group of sensors:
your PR does:
the software receives:
now determine which part of the string belong to which sensor ?
would produces results than can be added to each sensor. As I see it, your scan_group have a problem with:
I did not test it, but it is interesting to see what happens if a sensor is in a scan_group but specifies a different scan_interval…..at the very least it should be reported as a configuration error. I see a number of good enhancements in your PR, but to be honest your explanations confirm why scan_group is the more complicated solution compared to the master-slave idea. |
@janiversen Now I'm a bit confused.
So I simply can do the following:
where the address reflects the starting address of light. The same counts for discrete_input as we only deal with a bit value. The same counts for registers as we just define the starting address of a register and the number of registers to read, regardless of the underlying datatype (https://pymodbus.readthedocs.io/en/latest/source/library/pymodbus.html#module-pymodbus.register_read_message). So my suggestion was just to add a count reflecting the number of registers needed for a sensor to be able to determine to correct range of values to read. Looking at it more closing I saw that there is already a How we decode the messages from the read registers afterwards should be handled in the devices itself (binary_sensor, sensor, etc). The only difference of reading it in a batch or reading it one-but-one should then be the index of the register to start reading from. But maybe I didn't understand the documentation correctly? At least for discrete_inputs and coils the implementation works as expected. As for your concern of assigning a sensor to a scan_group and define a scan_interval: The first one overrule the second one, which means I only register the scan_interval in case there was no scan_group defined for this sensor:
What do you mean with:
Do you refer with group the type of devices? |
Coil is 1 bit, but not 1 but pr address. Doing read_coil address 17, count 2, returns 16bit coil1 is bit 0 and coil 2 is bit 1 My concern about the scan_interval is that the user should be informed. |
@janiversen |
Yes, that's true. But I read the results in the binary_sensor then by reading the correct bit of the provided offset-address. Means coil1 reads bit[0], coil2 reads bit[1]. As sayed, coils and discrete_inputs are working fine. t the moment I cannot proof that reading registers does work as I don't have such a device and I'm not yet able to run the tests. But I will keep an eye on this. |
I think discussing further in this PR does not lead to a better PR. The PR still include 2 integrations which are not allowed. I have tried to help make it a workable modbus solution by e.g. pointing at the problems in the current PR., but you have until now not make a single code change. Your scan_group is not a bad idea, buy you need to address the obvious problems and submit a PR with changes to the modbus integration only or follow the suggestions made in this PR. When I see such a PR I will do a proper review. |
i didn't do a code change yet because of two reasons:
To summarize I suggest to split this PR into multiple once with the following featureset:
@janiversen Maybe you can, as a last effort, agree with the above mentioned steps. After this I can close this one and will refer to it as soon as created new once. |
I cannot help you with your decision, I have outlined problems and potential solutions, but the solution is yours to make. In principle your steps sound logical. It is true I am skeptical mainly because I have shown you the problems but not received ideas/feedback on how to solve them. It is a feature eagerly waited for, so I am both open and positive, however I must ensure it works in all scenarios to avoid problems later. |
@janiversen I always asked back because I didn't and stil don't understand the mentioned adressing problem. But I will try to extract only the modbus changes without enocean support into a different branch and adapt the existing tests to test as well the same behaviour with scan_groups. So either this will help me to understand the problem or I can make another PR based on this. |
If you have solved the other problems you are far, I look forward to see how you solve the string problem among others. |
There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days. |
Breaking change
All changes should be backward compatible
Proposed change
This PR supports the following changes:
EnOcean:
Modbus:
I made all the changes in one PR to be able to guarantee that all current feature of my custom home control implementation ca be covered with an adjusted home assistant system. The current implementation is now up and running for two weeks and controls all the lights, covers and reads temperature and motion detection sensors as before.
As the enocean PR is not yet merged I cannot proceed to fully finish, test and integrate the PR but would like to now if the changes applied do conform to the strategy of home assistant.
Type of change
Additional information
Checklist
black --fast homeassistant tests
)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest
.requirements_all.txt
.Updated by running
python3 -m script.gen_requirements_all
..coveragerc
.The integration reached or maintains the following Integration Quality Scale:
To help with the load of incoming pull requests: