mrlt8 / docker-wyze-bridge

WebRTC/RTSP/RTMP/LL-HLS bridge for Wyze cams in a docker container
GNU Affero General Public License v3.0
2.49k stars 152 forks source link

Have the ability to use API/MQTT to change which waypoint to view #835

Open crslen opened 1 year ago

crslen commented 1 year ago

With the wyze cam pan v3 you can create up to 4 waypoints to see different views. Looking to have the ability to use API and/or MQTT to change which waypoint to view on the pan cam.

mrlt8 commented 1 year ago

Does the pan_cruise command work for that?

Or would it be more like what K11012_setCruisePoints seems to be doing?

crslen commented 1 year ago

Pan Cruise will switch waypoints automatically every 10 seconds. That K11012_setCruisePoints sounds more like what I'm looking for.

mrlt8 commented 1 year ago

This one is a little tricky because we need to send a list of positions, so it will probably need to be a json-based request. something like:

[{"vertical":"20","horizontal":"40","time":"10"},{"vertical":"20","horizontal":"120","time":"15"},{"vertical":"20","horizontal":"300","time":"10"}]

open to suggestions or ideas.

crslen commented 1 year ago

I'm using something similar to move the cameras left/right in home assistant with MQTT. If there's an option to just send the waypoint that is configured in the Wyze app that would be ideal. I'm not sure how to see what gets passed to the camera's from the app, or I would see whats happening to hopefully share more details.

mrlt8 commented 1 year ago

hmm maybe GET cruise_points and use that with SET ptz_position?

@jhansche might have some suggestions?

crslen commented 1 year ago

How do you find the commands that Wyze uses with the cameras?

jhansche commented 1 year ago

hmm maybe GET cruise_points and use that with SET ptz_position?

Yeah I'm pretty sure this is how the manual waypoint selection works. I don't think it's just a "set position to waypoint 1", etc. You have to get the waypoints and then set the position. At least that's my assumption. I didn't trace what it does when doing that, but I didn't see any command that selects the waypoint by index.

mrlt8 commented 1 year ago

Updated GET ptz_position and cruise_points to return a json response instead of the bytearray.

How do you find the commands that Wyze uses with the cameras?

most of the commands can be found by decompiling the wyze app and/or firmware.

mrlt8 commented 1 year ago

This should now be available in v2.3.0. Also updated the wiki with additional info.

a9s2w5 commented 1 year ago

Just a suggestion/idea.

It would be really beneficial if the bridge had the capability to act as a proxy for standard onvif ptz commands. That is to say, in people's software of choice, they could just select onvif and the generic PTZ commands could be sent to Wyze PAN cams, the bridge acting as the intermediary. I believe the generic onvif commands look something like this.

function`` relativeMove(device, { movement, speed }) {
  return device.services.ptz
    .relativeMove({
      ProfileToken: getProfileToken(device),
      Translation: {
        x: movement.x,
        y: movement.y,
        z: movement.z,
      },
      Speed: {
        x: speed.x,
        y: speed.y,
        z: speed.z,
      },
    })
    .then(getData);
}

  .example(
    "onvif-ptz move --baseUrl=http://192.168.0.123:8080 -u=admin -p=admin -x=0.01 -y=0.02 -z=0.03"

I just quickly copied this from marklagendijk/node-onvif-ptz-cli

If it's possible to standardize them, so they could be used in other apps, that would go a long way. And I believe the Onvif protocols pretty much have standardized pretty much everything. The usefulness of this would be a big deal for many, I believe. Imagine being able to use any of the software that's out there and use some of the many functions within the bridge.

mrlt8 commented 1 year ago

I believe onvif uses SOAP, however, I don't think there are any python-based onvif or soap libraries that can act as a server. Open to suggestions or ideas!

jhansche commented 1 year ago

Finally getting around to testing this, and it looks useful @mrlt8. A couple notes, not sure if intentional?

  1. The REST response is a JSON object where both .response and .value contain the current set of cruise/waypoints - should we expect both of them, or is one redundant? IOW if I were going to start using one, is there one that is more likely to "survive" long term?
  2. The response is currently a string. Is that expected? I was expecting the JSON response to be parsed and available as JSON, but as it is now we only get a string, so we would have to re-parse that string after obtaining it.
  3. Not only does it have to be re-parsed, but it's not even valid JSON that can be parsed. That is, I would expect it to be in array notation, using [..] instead of just a string of comma-separated JSON object strings. This is especially odd because the documentation requires that we post a new value as JSON data, but then the response doesn't adhere to that same requirement šŸ˜ž So in other words, we should be able to GET the current value, modify one field in the JSON, and then SET that same object back - but right now that's not possible.
$ curl 'http://localhost:5000/api/pan-1/cruise_points' | json_pp
{
   "command" : "cruise_points",
   "payload" : "",
   "response" : "{'vertical': 122, 'horizontal': 242, 'time': 0, 'blank': 10},{'vertical': 145, 'horizontal': 185, 'time': 0, 'blank': 10},{'vertical': 116, 'horizontal': 122, 'time': 0, 'blank': 10}",
   "status" : "success",
   "value" : "{'vertical': 122, 'horizontal': 242, 'time': 0, 'blank': 10},{'vertical': 145, 'horizontal': 185, 'time': 0, 'blank': 10},{'vertical': 116, 'horizontal': 122, 'time': 0, 'blank': 10}"
}

Unfortunately the weirdly encoded response/value string makes it harder to work with something like the RESTful integration, or the MQTT integration where we're already getting the parsed JSON object and maybe don't have an easy way to re-parse the string after we extract it from e.g. value_json.response.

Re (2) and (3), what I was expecting would be for at least one of those 2 response fields to be fully JSON-path addressable:

{
  "command": "cruise_points",
  "payload": "",
  "status": "success",

  // Could still keep one field as a string if you want to preserve the actual response from the camera/api:
  "response": "{'vertical': 122, 'horizontal': 242, 'time': 0, 'blank': 10},.....",

  // but if one could be pre-parsed as a JSON array, it would be much more convenient to use;
  // or a new field especially for the parsed-as-json value, similar to the HA MQTT/REST synthetic "value_json" field
  "response_as_json": [
    {
      "vertical": 122,
      "horizontal": 242,
      "time": 0,
      "blank": 10
    },
    {
      "vertical": 145,
      "horizontal": 185,
      "time": 0,
      "blank": 10
    },
    {
      "vertical": 116,
      "horizontal": 122,
      "time": 0,
      "blank": 10
    }
  ]
}

This way we can address those values using value_json.response_as_json[0].vertical, etc.

This would also make it more reliable if all we want to do is, say, adjust the horizontal value for one waypoint. We could easy do that if the value is already valid JSON:

var current = value_json.response_as_json
current[1].horizontal = current[1].horizontal + 10
// POST the modified "current" object back to MQTT/REST without having to touch the rest of the data

Unrelated, but the response from my camera highlights: the wiki page says blank can be "1 or 0, typically 0"; but mine reports blank: 10. Do you know what that indicates?

mrlt8 commented 12 months ago

Will try to work on this.

response is typically what the camera sends back, and value is what we try to use as the payload for the MQTT response. e.g. the camera would typically respond with a 1 for success when sending an off or 2 command, so the response would be 1 and the value would be 2 in this situation.

jhansche commented 12 months ago

Obviously if it's a lot of effort then maybe it's not worth it - we can still parse the value, splitting it by },{, but then we have to fix the broken json objects then, adding back the } and {; Or use regex, like ((\{.*?\})[,]?)*, to pull out the individual objects.

More than likely though, if it's not coming back as accessible JSON, then most people would probably just ignore this command entirely, and make their own script to just set ptz_position with predefined coordinates, without taking configured cruise points into account at all šŸ¤·ā€ā™‚ļø Arguably that's probably a more sane approach anyway, it's just more convenient to use the app to nudge the camera where you want it, and then save that as a waypoint. But if we can already get the current ptz_position, we can do that without waypoints.

Or if the goal is specifically to set the camera to a specific configured waypoint (which appears to be the original ask in this ticket), a much more approachable solution would be set_ptz_waypoint(index: int) taking 0-3 or 1-4, and let the add-on do the rest, parsing the configured waypoints, extracting the coordinates, and then set ptz_position to match (or raise if OOB). I for one would not want to go through the effort to manually extract these and switch to it, but if I can just click a button and have it switch to the waypoint I want, then it may be useful.

I guess it comes down to weighing the value added vs level of effort. I can see an argument for this being not worth the effort, and an easier proposal being to configure your own HA scripts to move to exact coordinates, and don't even worry about waypoints at all.

mrlt8 commented 12 months ago

@jhansche Could you try out the latest dev branch? I added a SET cruise_point topic that will look up a cruise point/waypoint based on the index number from K11010GetCruisePoints then send the coordinates to K11018SetPTZPosition.

jhansche commented 12 months ago

@mrlt8 It's a little slow to react (about 5.5 sec each time), but it works šŸ‘

mtwhitley commented 4 months ago

I believe onvif uses SOAP, however, I don't think there are any python-based onvif or soap libraries that can act as a server. Open to suggestions or ideas!

I know this is piggy-backing on a different topic, but I too am interested in ONVIF server support. I found this recent Python Soap Server library that I thought Iā€™d pass along in case it would help. It might be too simple or more of a POC; Iā€™m not sure. https://github.com/esmaily/python-soap-server

If it would be better for me to open a separate ticket for this enhancement request, Iā€™m happy to do that. Thanks!