Bezpečnostní kamery
AI na hlídání AI?
Bezpečnostní kamery jsou již běžné spotřební zboží — nelze je nějak využít pro mobilní robotiku? Jsou to „černé krabičky” pouze s mobilní aplikací a daty na cloudu nebo je alternativa? Blog update: 6/9/2024 — AbsoluteMove
Obsah
Blog
26. srpna 2024 — Náhodný výběr
Původně jsem to chtěl nazvat spíše „výběr kamery”, ale nabídka bezpečnostních kamer je široká a má trpělivost
cokoliv nakupovat je nerozumně omezená. Asi jediný požadavek byl outdoor s tím, že bych rád nějaké otevřené
API. Konzultoval jsem to s perplexity
a ve zkratce mi doporučila Reolink a ke kamerám
TP-Link Tapo C500 naznačovala, že existuje
PyTapo Python knihovna.
Teď zpětně trošku chápu, o čem to ta AI psala. Zatím jsem vybalil tu TP-Link Tapo C500, která podporuje pouze
WiFi přípojení a používá zdroj 12V/1A. Vyžadovalo to instalovat
TP-Link Tapo Android applikaci,
aby se kamera přepnula z access point módu na připojení na WiFi router. V rozšířených nastaveních lze pak
přidat nového uživatele s heslem. A s tím je spojeno to RTSP
(Real-Time Streaming Protocol):
- rtsp://username:password@camera_ip:554/stream1
… toto fungovalo vlastně ve VLC Media Playeru hned. Časem jsem dohledal, že
stream1 je s vyšším rozlišením a existuje ještě stream2 s nižším rozlišením. Stejné URL funguje i v
OpenCV a tím pádem i rovnou v OSGARovi
(stačí změnit v config/test-opencv-camera.json
port na URL výše). Jen v tom VLC je navíc slyšet i audio.
Další krok byl s kamerou pohnout. Co fungovalo bylo posouvání v aplikaci na telefonu (a stále streamovat video).
S tím je spojeno to druhé klíčové slovo ONVIF (Open Network Video Interface Forum).
A jelikož o tom nic nevím, tak jsem zase zkoušel perplexity.
Podle doporučení jsem nainstaloval zeep a onvif_zeep.
Ale příklad použití na Tapo C500 úplně nefungoval. První zrada byla s portem — místo 80 je třeba použít 2020,
jen už nevím, kde jsem tuto nápovědu sebral. Což mi ale připomíná, že jsem úplně vynechal PyTapo, které se zaseklo
na problému autorizace a přes to jsem se nedostal — stáhl si github repo,
jestli tam není novější verze, ale nepomohlo to. Také jsem zapomněl zmínit, že na kameře jsem po prvním připojení
upgradeoval firmware — to také mohlo rozbít aktuální pytapo. A také jsem nezmínil, že při dalším připojení
se změnila IP adresa kamery, ale to bych asi případně mohl změnit na routeru (aneb další možnost, jak se to mohlo pokazit).
from onvif import ONVIFCamera # Replace with your camera's IP address, port, username, and password camera_ip = '192.168.1.100' camera_port = 2020 username = 'admin' password = 'password' # Create an ONVIF camera object camera = ONVIFCamera(camera_ip, camera_port, username, password) # Get device information device_info = camera.devicemgmt.GetDeviceInformation() print("Manufacturer:", device_info.Manufacturer) print("Model:", device_info.Model) print("Firmware Version:", device_info.FirmwareVersion) print("Serial Number:", device_info.SerialNumber) print("Hardware ID:", device_info.HardwareId)
… toto fungovalo (se správnou IP, username a password)
Manufacturer: tp-link Model: Tapo C500 Firmware Version: 1.1.4 Build 240506 Rel.39487n Serial Number: a19999 (upraveno) Hardware ID: 1.0
ale při pokusech o posunutí se mi podařilo „položit server”, míněno server běžící na kameře:
capabilities = camera.devicemgmt.GetCapabilities()
skončilo s chybou:
http.client.BadStatusLine: POST /onvif/device_serviceHTTP/1.1Host 192.168.1.18:2020 User-Agent Zeep/4.2.1 (www.python-zeep.org)Accept-Encoding gzip, deflateAcHTTP/1.1 500 Internal Server Error
Pro dnešek asi stačí … časem bych doplnil i úvodní „omáčku” aneb motivace a pod.
p.s. thumb jsem převzal z https://www.railfanguides.us/oblivion/
6. září 2024 — AbsoluteMove
Toto se pokusím ještě teď v noci „odbavit” jako důkaz, že už nic nebrání nočnímu testování.
Posledně jsem psal, že dostat video z Tapo C500 bylo vlastně bez problémů (s použítím VLC).
Dobrá zpráva je, že je i poměrně triviální to streamované video nahrát (pokud je kamera na síti).
Co ale bylo (naštěstí už minulý čas) peklo bylo s tou kamerou programově pohnout! Jako ten ONVIF
je pěkná věc, ale to by to musel každý výrobce pořádně dodržovat. A jak jsem psal, že posledně to
skončilo 500 na serveru, tak s tím jsem dnes začal …
A k mému překvapení to není tak stará věc:
Jako myslím, že to novějším FW nevyřešili (mě to s 1.1.4 padá stále), ale zaujala mne tam další poznámka:
the "UtcTime" element should be returned in the ISO 8601 format., která pěkně korelovala s chybou:
ERROR:zeep.xsd.types.simple:Error during xml -> python translation Traceback (most recent call last): File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/isodate/isodatetime.py", line 51, in parse_datetime datestring, timestring = datetimestring.split('T') ValueError: not enough values to unpack (expected 2, got 1) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/zeep/xsd/types/simple.py", line 79, in parse_xmlelement return self.pythonvalue(xmlelement.text) File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/zeep/xsd/types/builtins.py", line 44, in _wrapper return func(self, re.sub(r"[\n\r\t ]", " ", value).strip()) File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/zeep/xsd/types/builtins.py", line 180, in pythonvalue return isodate.parse_datetime(value) File "/home/md/.virtualenvs/tapo/lib/python3.8/site-packages/isodate/isodatetime.py", line 53, in parse_datetime raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to" isodate.isoerror.ISO8601Error: ISO 8601 time designator 'T' missing. Unable to parse datetime string 'Fri Sep 6 20:52:11 2024'
Teď už si nevzpomenu, co jsem z té kamery chtěl dostat, asi
media = mycam.create_media_service() # Create ptz service object ptz = mycam.create_ptz_service() # Get target profile media_profile = media.GetProfiles()[0] # Get PTZ configuration options for getting continuous move range request = ptz.create_type('GetConfigurationOptions') request.ConfigurationToken = media_profile.PTZConfiguration.token ptz_configuration_options = ptz.GetConfigurationOptions(request)
… no nevím, měl jsem si to hned psát. Sigh.
Ale je to Python, takže stačilo oeditovat /home/md/.virtualenvs/tapo/lib/python3.8/site-packages/isodate/isodatetime.py, konkrétně
try: datestring, timestring = datetimestring.split('T') except ValueError: return datetime.strptime(datetimestring, "%a %b %d %H:%M:%S %Y") # raise ISO8601Error("ISO 8601 time designator 'T' missing. Unable to" # " parse datetime string %r" % datetimestring)
Další milník byl výpis ptz_configuration_options:
'Spaces': { 'AbsolutePanTiltPositionSpace': [ { 'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace', 'XRange': { 'Min': -1.0, 'Max': 1.0 }, 'YRange': { 'Min': -1.0, 'Max': 1.0 } } ], 'AbsoluteZoomPositionSpace': [], 'RelativePanTiltTranslationSpace': [ { 'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace', 'XRange': { 'Min': -1.0, 'Max': 1.0 }, 'YRange': { 'Min': -1.0, 'Max': 1.0 } } ], 'RelativeZoomTranslationSpace': [], 'ContinuousPanTiltVelocitySpace': [], 'ContinuousZoomVelocitySpace': [], 'PanTiltSpeedSpace': [ { 'URI': 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace', 'XRange': { 'Min': 0.0, 'Max': 1.0 } } ], 'ZoomSpeedSpace': [], 'Extension': None, '_attr_1': None, … }, 'PTZTimeout': { 'Min': datetime.timedelta(seconds=1), 'Max': datetime.timedelta(seconds=600) }, '_value_1': [], 'PTControlDirection': None, 'Extension': None, '_attr_1': None }
Nyní mám zpětně teorii, že ty prázdné listy znamenají, že to ta kamera prostě neumí. Ale co by měla umět je AbsolutePanTiltPositionSpace!
Jenom k tomu dohledat, co že je to klíčové slovo, které se má někde vyplnit? Kupodivu to je AbsoluteMove.
# positionrequest = ptz.create_type('AbsoluteMove') positionrequest.ProfileToken = media_profile.token if positionrequest.Position is None: positionrequest.Position = ptz.GetStatus({'ProfileToken': media_profile.token}).Position positionrequest.Position.PanTilt.space = ptz_configuration_options.Spaces.AbsolutePanTiltPositionSpace[0].URI # positionrequest.Position.Zoom.space = ptz_configuration_options.Spaces.AbsoluteZoomPositionSpace[0].URI if positionrequest.Speed is None: positionrequest.Speed = ptz.GetStatus({'ProfileToken': media_profile.token}).Position positionrequest.Speed.PanTilt.space = ptz_configuration_options.Spaces.PanTiltSpeedSpace[0].URI request = positionrequest request.Position.PanTilt.x = x request.Position.PanTilt.y = y ptz.AbsoluteMove(request)
Z velké části opsáno z FalkTannhaeuser/python-onvif-zeep.
Ten Zoom je třeba zakomentovat/odstranit, protože C500 žádný zoom nemá. A výsledek po mini wrapperu a volání z konzole:
(tapo) md@md-ThinkPad-P50:~/git/pytapo$ python patapo.py 0.5 -0.5 (tapo) md@md-ThinkPad-P50:~/git/pytapo$ python patapo.py -0.4 -0.5 (tapo) md@md-ThinkPad-P50:~/git/pytapo$ python patapo.py 0 -0.7
… viz noční video – aréna je přípravena!