8000 Refactor zwave discovery to entity schema by emlove · Pull Request #6445 · home-assistant/core · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Refactor zwave discovery to entity schema #6445< 8000 /span>

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

Merged
merged 4 commits into from
Mar 13, 2017
Merged

Refactor zwave discovery to entity schema #6445

merged 4 commits into from
Mar 13, 2017

Conversation

emlove
Copy link
Contributor
@emlove emlove commented Mar 6, 2017

Description:

This PR is a big overhaul to the zwave discovery system. Currently, the discovery code is focused around matching to a single zwave value, and then creates an entity object. If this entity requires access to multiple zwave values on a node, the current code searches all node values for the matching value each time it needs to be accessed.

This change creates a discovery schema that allows each entity to describe all of the values it will need to access ahead of time. As values are added to the network, they are checked against this schema and associated with entities as necessary. Then, when the entity needs access, it only needs to check if the value has been discovered or not. It is also possible to require multiple associated values to be discovered before creating the entity, although this is not yet in use.

The core of this functionality comes from the new class ZWaveDeviceEntityValues. This class aggregates all values that should be associated to one entity, and provides easy access to these values by schema name.

I've tested with the devices I have, but I'll need help making sure that everything works with this change.

Testing Checklist:

  • Binary Sensor
  • Climate
  • Cover: Rollershutter
  • Cover: Garage Door
  • Light
  • Light: Color
  • Lock
  • Sensor
  • Switch

@mention-bot
Copy link

@armills, thanks for your PR! By analyzing the history of the files in this pull request, we identified @turbokongen, @balloob and @andrey-git to be potential reviewers.

"""Get the values for a given command_class with arguments.
def check_node_schema(node, schema):
"""Check if node matches the passed node schema."""
if (const.DISC_NODE_ID in schema and
Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to make the schemas voluptuous schemas and just validate here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's something I considered, and I could go either way on it. It would mean schema definitions would be a little more verbose, since almost everything would include vol.In( wrapping the requirements. We would then gain some more flexibility though if we wanted to do more advanced selection later.

Copy link
Contributor

Choose a reason for hiding this comment

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

voluptuous is good for validating user input.
For code I think it is better to make it more compact and validate it with tests.

Copy link
Member

Choose a reason for hiding this comment

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

I think voluptuous seems a perfect fit for what's needed here. The validators can be written modularly. We would also not need this long trail of if and return statement, which I don't think is nice python.

Validating is not about if the code works in this case, it's to choose logic path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In my opinion, I'd rather have the longer schema checking functions, but then more concise schema definitions, than sacrifice schema readability to make the checking functions more readable. The schemas are already a pretty big word soup as it is. The util functions will pretty much be ignored after this PR, but we'll be working in the schema definitions a lot.

I also think that voluptuous looks perfect on the surface, but isn't exactly what we need. Since values are coming in one at a time, we have to extract the individual value schemas from the definition and test against those. We might end up fighting the voluptuous normal usage patterns to get it working.

@andrey-git andrey-git self-requested a review March 6, 2017 14:08
Copy link
Contributor
@andrey-git andrey-git left a comment

Choose a reason for hiding this comment

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

Thanks for you awesome PR!

One change in behavior I see here is that everything (except primary value) used to be optional. Now entity won't be created until all non-optional dependencies are satisfied.

I think this should be merged after 0.40 is cut to let this soak in dev as much as possible.

"""Get the values for a given command_class with arguments.
def check_node_schema(node, schema):
"""Check if node matches the passed node schema."""
if (const.DISC_NODE_ID in schema and
Copy link
Contributor

Choose a reason for hiding this comment

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

Python if doesn't require parentheses (also elsewhere)h

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They're used here as an alternative to backslash line continuation.

self.set_value(
class_id=zwave.const.COMMAND_CLASS_THERMOSTAT_SETPOINT,
index=self._index, data=temperature)
self.values.primary.data = temperature
self.schedule_update_ha_state()
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe better left for a separate PR - but I think this call is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably right. It should get updated on the value changed callback. I'd rather leave that separate though, since I don't have a climate device to test with.

zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL
and values.primary.index == 0):
return ZwaveRollershutter(values)
elif (values.primary.command_class ==
Copy link
Contributor

Choose a reason for hiding this comment

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

elif values.primary.command_class in [
    zwave.const.COMMAND_CLASS_SWITCH_BINARY,
    zwave.const.COMMAND_CLASS_BARRIER_OPERATOR]:

alarm_level = self.get_value(class_id=zwave.const
.COMMAND_CLASS_ALARM,
label=['Alarm Level'], member='data')
alarm_level = self.values.alarm_level and self.values.alarm_level.data
Copy link
Contributor

Choose a reason for hiding this comment

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

Won't this make alarm_level boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the first expression evaluates to True, it will return the second expression, although I agree it should be rewritten to be less confusing.

component = workaround_component

name = "{}.{}".format(component, object_id(value))
name = "{}.{}".format(
Copy link
Contributor

Choose a reason for hiding this comment

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

The name here is pre-workaround (which can change component and thus name)

Would it be possible to use post-workaround name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great catch! I think what will be best is just passing a reference to device_config to ZWaveDeviceEntityValues, then recheck the node config in the equivalent location when creating the new entity.

@emlove
Copy link
Contributor Author
emlove commented Mar 6, 2017

I definitely agree about saving this for 0.41.

Right now in the schema I've defined all values other than the primary as optional, to mimic the old behavior. Once we've worked out the kinks we can come back and review each platform for what makes sense.

class_id=[const.COMMAND_CLASS_SENSOR_MULTILEVEL,
const.COMMAND_CLASS_METER],
label=['Power'], member='value_id',
instance=self._value.instance)
Copy link
Contributor

Choose a reason for hiding this comment

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

Power consumption should be read from a value with same instance as the primary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right now I have ZWaveDeviceEntityValues require all associated values to have the same instance. This is a little more aggressive on instance checking than previously for some of the other associations, but it should be the right thing to do. https://github.com/home-assistant/home-assistant/pull/6445/files#diff-34736f9c4da86b6334cf5d201370707fR833

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I missed that.
It would remove battery and wakeup from "subdevices" as there is only a single instance of those.

I guess that's OK.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I suppose I'll have to make it a little more advanced. Any preference on how "same instance as primary" should look in the schema? Maybe we have separate sections for node values and instance values?

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I have plans to make per-Node zwave entities with more data and functionality. Then wakeup and battery would move there anyway.

So let's keep the "always same instance" rule you had in the first version.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see you already solved this. Great!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we can always refine this later, but this at least gives us a jumping off point.

Thanks again for the great feedback!

@balloob
Copy link
Member
balloob commented Mar 7, 2017

Just going to link #6437 here.

}


DISCOVERY_SCHEMAS = [
Copy link
Member

Choose a reason for hiding this comment

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

We should consider moving this to it's own file.

@@ -752,24 +823,166 @@ def start_zwave(_service_or_event):
return True


class ZWaveDeviceEntityValues():
Copy link
Member

Choose a reason for hiding this comment

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

Let's move this to a new file to help with dealing with values.

Copy link
Contributor Author
@emlove emlove Mar 7, 2017

Choose a reason for hiding this comment

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

So I started this, but I think it will create more of a mess than it will solve. The logic to build the entity uses a lot of module-level information from zwave that would need to be moved out of place even further to avoid circular dependencies. For example, we use DOMAIN, CONF_IGNORED, CONF_POLLING_INTENSITY, DATA_ZWAVE_DICT, and the object_id helper.

@emlove
Copy link
Contributor Author
emlove commented Mar 7, 2017

I agree about the new tests. I'll try to get some new tests written in a separate PR, since they should pass the same with or without this one. Plan is still to wait until after 0.40 for this PR. @andrey-git Let's move test planning to #6437.

const.DISC_GENRE: const.GENRE_USER,
}})},
]

Choose a reason for hiding this comment

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

blank line at end of file

self._open_id = self.values.close.value_id
self._close_id = self.values.open.value_id
self._workaround = None
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

This will remove the workaround on 2nd update.

The main if should check the _open_id & _clode_id are not set yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks again! This one seems like a good candidate for extra required values. If open/close are both required, we could just check the workaround in init, and make this code much simpler. But like I said, for this PR I want to minimize any extra side-effects until this change has more time to be tested.

@andrey-git
Copy link
Contributor

Except for the reverse open-close workaround I commented on - binary_sensor, sensor, switch, light & Rollershutter seem to work fine.

I don't have lock & climate.

@andrey-git
Copy link
Contributor

I think we should merge this now and add tests / other improvements from here.
@balloob WDYT?

@balloob
Copy link
Member
balloob commented Mar 13, 2017

Sounds good 👍

@balloob balloob merged commit 56abc7f into home-assistant:dev Mar 13, 2017
balloob added a commit that referenced this pull request Mar 13, 2017
balloob added a commit that referenced this pull request Mar 13, 2017
balloob added a commit that referenced this pull request Mar 13, 2017
@balloob
Copy link
Member
balloob commented Mar 13, 2017

Bah. The PR was outdated and crashed all the tests. I have reverted it.

@emlove
Copy link
Contributor Author
emlove commented Mar 13, 2017

Ah, well silver lining the tests work, right? I'll get this updated tonight.

@emlove emlove deleted the zwave-entity-schema branch March 14, 2017 01:32
emlove pushed a commit that referenced this pull request Mar 14, 2017
* Revert "Revert "Refactor zwave discovery to entity schema (#6445)" (#6564)"

This reverts commit 58826b2.

* Update zwave tests for enitity schema

* Fix merge error

* Switch dict_id to id(self)
@balloob balloob mentioned this pull request Mar 24, 2017
@home-assistant home-assistant locked and limited conversation to collaborators Jul 17, 2017
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.

7 participants
0