Skip to content

Commit

Permalink
Merge branch 'main' into firmware
Browse files Browse the repository at this point in the history
  • Loading branch information
starkillerOG committed Nov 4, 2024
2 parents 020af3f + 62d0efc commit 55344cd
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 77 deletions.
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ await host.set_siren(0, True)
await host.subscribe('http://192.168.1.11/webhook123')
# After some minutes check the renew timer (keep the eventing alive):
if (host.renewTimer <= 100):
if (host.renewtimer() <= 100):
await host.renew()
# Logout and disconnect
await host.disconnect()
await host.logout()
````

### Example
Expand All @@ -104,6 +104,39 @@ if __name__ == "__main__":
asyncio.run(print_mac_address())
````

### TCP push event example
This is an example of how to receive TCP push events.
The callback will be called each time a push is received.
The state variables of the Host class will automatically be updated when a push comes in.
````
from reolink_aio.api import Host
import asyncio
import logging
logging.basicConfig(level="INFO")
_LOGGER = logging.getLogger(__name__)
def callback() -> None:
_LOGGER.info("Callback called")
async def tcp_push_demo():
# initialize the host
host = Host(host="192.168.1.109", username="admin", password="admin1234")
# connect and obtain/cache device settings and capabilities
await host.get_host_data()
# Register callback and subscribe to events
host.baichuan.register_callback("unique_id_string", callback)
await host.baichuan.subscribe_events()
# Process TCP events for 2 minutes
await asyncio.sleep(120)
# unsubscribe and close the device connection
await host.baichuan.unsubscribe_events()
await host.logout()
if __name__ == "__main__":
asyncio.run(tcp_push_demo())
````

### Acknowledgment
This library is based on the work of:
- @fwestenberg: https://github.com/fwestenberg/reolink_dev
Expand Down
42 changes: 28 additions & 14 deletions reolink_aio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,10 +656,10 @@ def camera_name(self, channel: int | None) -> str:
if channel is None:
return self.nvr_name

if channel not in self._channel_names and channel in self._stream_channels and channel != 0:
if not self.is_nvr and channel not in self._channel_names and channel in self._stream_channels and channel != 0:
return self.camera_name(0) # Dual lens cameras
if channel not in self._channel_names:
if len(self._channels) == 1:
if not self.is_nvr:
return self.nvr_name
return "Unknown"
return self._channel_names[channel]
Expand Down Expand Up @@ -692,7 +692,7 @@ def camera_online(self, channel: int) -> bool:
def camera_model(self, channel: int | None) -> str:
if channel is None:
return self.model
if channel not in self._channel_models and channel in self._stream_channels and channel != 0:
if not self.is_nvr and channel not in self._channel_models and channel in self._stream_channels and channel != 0:
return self.camera_model(0) # Dual lens cameras
if channel not in self._channel_models:
return "Unknown"
Expand All @@ -701,7 +701,7 @@ def camera_model(self, channel: int | None) -> str:
def camera_hardware_version(self, channel: int | None) -> str:
if channel is None:
return self.hardware_version
if channel not in self._channel_hw_version and channel in self._stream_channels and channel != 0:
if not self.is_nvr and channel not in self._channel_hw_version and channel in self._stream_channels and channel != 0:
return self.camera_hardware_version(0) # Dual lens cameras
if channel not in self._channel_hw_version:
if not self.is_nvr:
Expand Down Expand Up @@ -1298,12 +1298,12 @@ async def _login_try_ports(self) -> None:
pass

# update info such that minimum firmware can be assest
self._nvr_model = self.baichuan.model
self._nvr_hw_version = self.baichuan.hardware_version
self._nvr_item_number = self.baichuan.item_number
self._nvr_sw_version = self.baichuan.sw_version
if self.baichuan.sw_version is not None:
self._nvr_sw_version_object = SoftwareVersion(self.baichuan.sw_version)
self._nvr_model = self.baichuan.model()
self._nvr_hw_version = self.baichuan.hardware_version()
self._nvr_item_number = self.baichuan.item_number()
self._nvr_sw_version = self.baichuan.sw_version()
if self.baichuan.sw_version() is not None:
self._nvr_sw_version_object = SoftwareVersion(self.baichuan.sw_version())

# retry login now that the port is open, this will also logout the baichuan session
try:
Expand Down Expand Up @@ -1655,6 +1655,10 @@ def construct_capabilities(self, warnings=True) -> None:
self._capabilities[channel].add("ptz_guard")
if self.api_version("GetPtzCurPos", channel) > 0:
self._capabilities[channel].add("ptz_position")
if self.ptz_pan_position(channel) is not None:
self._capabilities[channel].add("ptz_pan_position")
if self.ptz_tilt_position(channel) is not None:
self._capabilities[channel].add("ptz_tilt_position")
if ptz_ver in [2, 3]:
self._capabilities[channel].add("ptz_speed")
if channel in self._ptz_presets and len(self._ptz_presets[channel]) != 0:
Expand Down Expand Up @@ -2283,6 +2287,15 @@ async def get_host_data(self) -> None:

self.map_channels_json_response(json_data, channels)

# Baichuan fallbacks
for channel in self._channels:
if self.camera_hardware_version(channel) == "Unknown":
try:
await self.baichuan.get_info(channel)
except ReolinkError:
continue
self._channel_hw_version[channel] = self.baichuan.hardware_version(channel)

# Let's assume all channels of an NVR or multichannel-camera always have the same versions of commands... Not sure though...
def check_command_exists(cmd: str) -> int:
for x in json_data:
Expand Down Expand Up @@ -4059,12 +4072,13 @@ async def set_ptz_command(self, channel: int, command: str | None = None, preset

await self.send_setting(body)

def ptz_pan_position(self, channel: int) -> int:
def ptz_pan_position(self, channel: int) -> int | None:
"""pan position"""
if channel not in self._ptz_position:
return 0
return self._ptz_position.get(channel, {}).get("PtzCurPos", {}).get("Ppos")

return self._ptz_position[channel]["PtzCurPos"]["Ppos"]
def ptz_tilt_position(self, channel: int) -> int | None:
"""tilt position"""
return self._ptz_position.get(channel, {}).get("PtzCurPos", {}).get("Tpos")

def ptz_guard_enabled(self, channel: int) -> bool:
if channel not in self._ptz_guard_settings:
Expand Down
Loading

0 comments on commit 55344cd

Please sign in to comment.