feat: introduce TopicFilter support#142
Conversation
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
|
Thank you @matthax you've helped me figure out why I couldn't get I kept getting errors like and I ended up on this PR and I also had the same error with your implementation. This is when I figured out something else must have been wrong instead of my payload and I ended up editing
and it worked right away! Just though I would share! In my case I went for a simpler approach that is equivalent to yours I believe if filter_expression:
topic_expr_element = self.client.get_element("wsnt:TopicExpression")
topic_expr_type = self.client.get_type("wsnt:TopicExpressionType")
topic_expr = topic_expr_type(
filter_expression, Dialect="http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"
)
any_topic_expr = AnyObject(topic_expr_element, topic_expr)
message["Filter"] = {"_value_1": [any_topic_expr]}I am not sure why the definitions of b-2.xsd are different for |
Ah! I should have thought of just editing the file, this is much cleaner as an approach, nice work! Maybe we just need that change in the upstream onvif library then and I could clean up the manual construction I'm doing? |
You don't need to edit the file, I've actually extracted the definition from https://github.com/openvideolibs/python-onvif-zeep-async/blob/async/onvif/wsdl/b-2.xsd#L51 because the latest version here http://docs.oasis-open.org/wsn/b-2.xsd appears to not be working. My demo should work for python-onvif-zeep-async as well without any extra work. In case it complains about not finding the namespace client.set_ns_prefix("wsnt", "http://docs.oasis-open.org/wsn/b-2") |
bdraco
left a comment
There was a problem hiding this comment.
codecov/patchFailing after 1s — 56.00% of diff hit (target 68.83%)
Please complete test coverage
|
@bluetoothbot review |
On cleaning up the manual construction: you actually don't need any upstream/xsd change for this repo. The bundled The Two concrete suggestions while you're here: because that |
PR Review — feat: introduce TopicFilter supportThe topic-filter feature is a useful addition and the 🔴 Blocking1. Minimum-interval clamp is inverted — caps the interval instead of enforcing a floor (`onvif/managers.py`, L58-62)This logic is backwards and does the opposite of what the PR intends. Walk through the current expression:
So it fails to enforce the minimum and it caps every interval above 60s down to 60s. That second effect is a regression for all existing callers: downstream consumers (e.g. Home Assistant's ONVIF integration) pass intervals far larger than a minute, and this change would force them to renew roughly every ~48s (60s × Use a floor instead: self._interval = max(interval, MINIMUM_SUBSCRIPTION_INTERVAL)Note this mirrors the existing, correct pattern already in Also note this clamp lives in 2. New TopicExpression / filter paths are untested (blocking per codecov + review) (`onvif/types.py`, L107-127)@bdraco requested changes and codecov is failing (56% of diff covered vs 68.83% target). None of the new logic is exercised:
🟡 Important1. topic_filter annotation contradicts client.py and the actual usage (`onvif/managers.py`, L306)The parameter is annotated
A caller passing an actual 2. Filter validated as XPath but always sent with the ConcreteSet dialect (`onvif/managers.py`, L325-328)
Two follow-on points:
🟢 Suggestions1. Filter `_value_1` may need to be a list (`onvif/managers.py`, L349-352)
Checklist
SummaryThe topic-filter feature is a useful addition and the To rebase specific severity levels, mention me: Automated review by Kōan340e9ea |
Plumbs an optional topic_filter through create_pullpoint_manager so callers can subscribe to a subset of events via a WS-Topic expression (defaults to the ONVIF ConcreteSet dialect). Addresses the review feedback on the original PR: * Enforce the minimum subscription interval as a floor instead of a cap. The previous expression `interval if interval <= MIN else MIN` silently truncated every interval over 60s, a regression for existing callers. Replaced with `max(interval, MINIMUM_SUBSCRIPTION_INTERVAL)`. * Type topic_filter as `str | None` to match the actual value used. * Drop the lxml XPath validator (which never accepted ONVIF prefixed topic names like `tns1:Foo` anyway) and expose `topic_filter_dialect` so the grammar matches what is sent on the wire. Default remains ConcreteSet. * Serialise `Filter._value_1` as a list per the WS-Notification FilterType schema (xs:any maxOccurs="unbounded"). * Reject blank topic_filter strings up front with a clear ValueError rather than silently sending an empty Filter to the camera. Tests: * `tests/test_managers.py`: unit-tests for the interval clamp (including a regression test for the inverted cap) and topic_filter validation. * `tests/test_types.py`: covers TopicExpression construction and custom-dialect handling via a real zeep client. * `tests/test_hikvision_e2e.py`: end-to-end coverage that drives create_pullpoint_manager against the fake Hikvision camera and asserts on the serialised Filter / TopicExpression bytes, plus a regression test that no Filter element is sent when topic_filter is omitted. * `tests/fake_hikvision.py`: serve Unsubscribe / Renew on /onvif/Subscription so the e2e tests can exercise the full manager lifecycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8ad2a72 to
b5a888a
Compare
Rebase follow-ups after porting openvideolibs#142 onto async: - assign the ValueError message to a variable (ruff EM101) - update test_create_pullpoint_manager for the new topic_filter / topic_filter_dialect arguments forwarded to PullPointManager

Hey, I'll start by saying I'm new to this and I just ran into an issue where I could not control topic subscriptions, so I've tried to add it a few ways and I wanted to bring that to this PR and get some feedback.
My first attempt was to simply set the
Filtersomething likeThis doesn't break, but it doesn't actually work. The actual definition can be found in the spec
So I added a constructor to
types.pybut I'm not sure that's really right, and the reason is because I'm relying on the zeep client to actually build theTopicExpressionType. Maybe there's a better way to do this without requiring the client binding?But, this approach does make it easy for clients to call in, you just specify a filter as a string, and the dialect is set automatically for you. Painless, but you lose the ability to specify a different dialect (This could also be added as a parameter to the constructor too, if you'd prefer that). It also makes it a little easier to do some validation, (e.g. the
XPATHis valid syntactically).Another approach would be to allow the filter to be passed in fully, and make it the callers problem to build the
TopicExpression. Maybe it's still worthwhile to provide a constructor or utility to make life easier, but that would give callers the ability to set their own dialect and move the whole zeep client dependency coupling totally out of the manager. It's also worth noting that cameras withFixedTopicSet = Truedon't seem to actually supportXPATHs, they expect a specific selection. If the selection is invalid, you'll get a pretty gnarly exception. In my application I useGetEventPropertiesto validate the selection, it didn't seem to be something that would really fit in the manager class, but if you wanted that as an example or something I could probably pull it together.Let me know what you think would be the right approach or if I can make any changes, just thought it was useful to limit the subscriptions.
I also fixed an issue with the
MINIMUM_SUBSCRIPTION_SECONDS, I should probably make that a different PR but if you specify an interval under the 60s, the subscription won't renew in time, subsequent calls fail on connection timeouts and things are sad. I just enforced the minimum to get around this.