rroller / dahua

Dahua Camera and Doorbell Home Assistant Integration
MIT License
382 stars 74 forks source link

Support for iMou devices (DAHUA based) #6

Open marcinbauer85 opened 3 years ago

marcinbauer85 commented 3 years ago

Is there chance of this working with imou Ranger 2 cameras, which are based on DAHUA IPC-A22EP?

sagarspatil commented 3 years ago

Port 80. DHIP.

[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] Opening connection to 10.10.10.85 on port 80: Done
[+] Login: Success
[BEGIN SEND (10.10.10.85)] <------------------2259------------------>
20000000|44484950|00000000|00000000|b8000000|00000000|b8000000|00000000
{"id": 0, "method": "global.login", "params": {"clientType": "", "ipAddr": "127.0.0.1", "loginType": "Direct", "password": "", "userName": "admin", "Encryption": "None"}, "session": 0}
[ END  SEND (10.10.10.85)] <------------------2259------------------>
[BEGIN RECV (10.10.10.85)] <------------------2379------------------>
20000000|44484950|8d6d04ab|00000000|eb000000|00000000|eb000000|00000000
{"result":false,"params":{"realm":"Login to 907F3C6C73FEDA77","random":"1f1e4fbc-858a-47c8-8a50-cb9310ad48cb","encryption":"Default"},"error":{"code":268632079,"message":"Component error: login challenge!"},"id":0,"session":2869194125}
[ END  RECV (10.10.10.85)] <------------------2379------------------>
[BEGIN SEND (10.10.10.85)] <------------------2259------------------>
20000000|44484950|8d6d04ab|01000000|17010000|00000000|17010000|00000000
{"id": 1, "method": "global.login", "params": {"userName": "admin", "password": "9E44A9BF0C9D7F1AD639190D1CBD7B4A", "clientType": "", "ipAddr": "127.0.0.1", "loginType": "Direct", "authorityInfo": "", "authorityType": "Default", "passwordType": "Default"}, "session": 2869194125}
[ END  SEND (10.10.10.85)] <------------------2259------------------>
[BEGIN RECV (10.10.10.85)] <------------------2379------------------>
20000000|44484950|8d6d04ab|01000000|4d000000|00000000|4d000000|00000000
{"result":true,"params":{"keepAliveInterval":30},"id":1,"session":2869194125}
[ END  RECV (10.10.10.85)] <------------------2379------------------>
[BEGIN SEND (10.10.10.85)] <------------------2259------------------>
20000000|44484950|8d6d04ab|05000000|4c010000|00000000|4c010000|00000000
{"id": 5, "method": "system.multicall", "params": [{"method": "magicBox.getDeviceType", "params": null, "session": 2869194125, "id": 2}, {"method": "magicBox.getDeviceClass", "params": null, "session": 2869194125, "id": 3}, {"method": "global.getCurrentTime", "params": null, "session": 2869194125, "id": 4}], "session": 2869194125}
[ END  SEND (10.10.10.85)] <------------------2259------------------>
[BEGIN RECV (10.10.10.85)] <------------------2379------------------>
20000000|44484950|8d6d04ab|05000000|d8000000|00000000|d8000000|00000000
{"result":true,"params":[{"result":true,"id":2,"params":{"type":"IPC-A22E"}},{"result":true,"id":3,"params":{"type":"IPC"}},{"result":true,"id":4,"params":{"time":"2021-07-08 22:13:15"}}],"id":5,"session":2869194125}
[ END  RECV (10.10.10.85)] <------------------2379------------------>
[*] Remote Model: IPC-A22E, Class: IPC, Time: 2021-07-08 22:13:15
[BEGIN SEND (10.10.10.85)] <------------------2259------------------>
20000000|44484950|8d6d04ab|06000000|50000000|00000000|50000000|00000000
{"method": "system.listService", "session": 2869194125, "params": null, "id": 6}
[ END  SEND (10.10.10.85)] <------------------2259------------------>
[BEGIN RECV (10.10.10.85)] <------------------2379------------------>
20000000|44484950|8d6d04ab|06000000|65000000|00000000|65000000|00000000
{"result":false,"error":{"code":268632064,"message":"InterfaceNotFound"},"id":6,"session":2869194125}
[ END  RECV (10.10.10.85)] <------------------2379------------------>
[BEGIN SEND (10.10.10.85)] <------------------2259------------------>
20000000|44484950|8d6d04ab|07000000|4b000000|00000000|4b000000|00000000
{"method": "global.logout", "params": null, "session": 2869194125, "id": 7}
[ END  SEND (10.10.10.85)] <------------------2259------------------>
[BEGIN RECV (10.10.10.85)] <------------------2379------------------>
20000000|44484950|8d6d04ab|07000000|39000000|00000000|39000000|00000000
{"result":true,"params":null,"id":7,"session":2869194125}
[ END  RECV (10.10.10.85)] <------------------2379------------------>
[+] Logout
[*] Closed connection to 10.10.10.85 port 80
[*] All done
[+] Successful instance termination of IPC-A22E (10.10.10.85)
mcw0 commented 3 years ago

@gelokatil

Remote Model: IPC-A22E, Class: IPC, Time: 2021-07-08 17:28:20 This tells me JSON do work

same with this: [Console] # device [+] [System] Vendor: Lechange, Build: 2020-07-30 19:17:25, Version: 2.680.0000000.9.R Device: IPC-A22P-S2, Web: 3.2.1.35284, OEM: 0000000 Package: LC_IPC-Consumer-Hai-Edison_Eng_P

However, your crash are most probably something made by me while trying to parse incoming JSON.

I can also see you are able to attach to the "Console", however you do not get any output from the "builtin Coonsole", only my stuff.

You could try with the command: setDebug: (This is needed for VTH and VTO before giving some output)

mcw0 commented 3 years ago

Port 80. DHIP.

Cool!

DHIP working, so HTTP/HTTPS with JSON should work just fine as well.

Below you find the raw HTTP/HTTPS part of my next script. Trying/working on to split up stuff to get better overview and functionality. Beware, will not just work out of the box, just to give some ideas before I am done with other parts of my script.

@rroller @sagarspatil @gelokatil (et all) Hope it will help you out in someway...


from requests import packages from requests.packages import urllib3 from requests.packages.urllib3 import exceptions

class DahuaHttp(object): def del(self): log.info('DahuaHttp DELETE')

""" Dahua http """
def __init__(self, rhost, rport, proto, timeout=60):
    super(DahuaHttp, self).__init__()

    self.rhost = rhost
    self.rport = rport
    self.proto = proto
    self.timeout = timeout

    self.remote = None
    self.uri = None
    self.stream = None

    """ Most devices will use self-signed certificates, suppress any warnings """
    requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

    self.remote = requests.Session()

    """Used with _debug"""
    self.headers = self.remote.headers
    self.cookies = self.remote.cookies

    self._init_uri()

    import random as random_agent
    random_agent.seed(1)
    self.remote.headers.update({
        'User-Agent': useragents.random(),
        'X-Requested-With': 'XMLHttpRequest',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Language': 'en-US,en;q=0.9',
        'Host': '{}:{}'.format(self.rhost, self.rport),
    })
    """
    self.remote.proxies.update({
        # 'http': 'http://127.0.0.1:8080',
    })
    """

def send(self, url=None, query_args=None, login=False, timeout=5):

    """JSON API communication"""
    if query_args:
        if query_args.get('params') is not None and not len(query_args.get('params')):
            query_args.update({'params': None})

    """This weird code will try automatically switch between http/https
    and update Host
    """
    try:
        if url and not query_args:
            return self.get(url, timeout)
        else:
            dh_data = self.post(self._get_url(login, url), query_args, timeout)
    except requests.exceptions.ConnectionError:
        self.proto = 'https' if self.proto == 'http' else 'https'
        self._init_uri()
        try:
            if url and not query_args:
                return self.get(url, timeout)
            else:
                dh_data = self.post(self._get_url(login, url), query_args, timeout)
        except requests.exceptions.ConnectionError as e:
            if login:
                return self._error(dh_error=e)
            return None
    except requests.exceptions.RequestException as e:
        if login:
            return self._error(dh_error=e)
        return None
    except KeyboardInterrupt:
        return None

    """302 when requesting http on https enabled device"""
    if dh_data.status_code == 302:
        redirect = dh_data.headers.get('Location')
        self.uri = redirect[:redirect.rfind('/')]
        self._update_host()
        if url and not query_args:
            return self.get(url, timeout)
        else:
            dh_data = self.post(self._get_url(login, url), query_args, timeout)

    """Catch non dahua hosts"""
    if not dh_data.status_code == 200:
        return self._error(dh_error=dh_data.text, code=dh_data.status_code)

    """JSON API communication"""
    dh_json = dh_data.json()

    """Set SessionID Cookie during login"""
    if login and self.remote.cookies.get('DWebClientSessionID') is None:
        self.remote.cookies.set('username', query_args.get('params').get('userName'))
        self.remote.cookies.set('DWebClientSessionID', dh_json.get('session'))

    return dh_data

@staticmethod
def _get_url(login, url):
    if login:
        return '/RPC2_Login'
    elif url:
        """GET or other POST JSON API communication"""
        return url
    """Default JSON API communication"""
    return '/RPC2'

def _update_host(self):
    if not self.remote.headers.get('Host') == self.uri[self.uri.rfind('://') + 3:]:
        self.remote.headers.update({
            'Host': self.uri[self.uri.rfind('://') + 3:],
        })

def _init_uri(self):
    self.uri = '{proto}://{rhost}:{rport}'.format(proto=self.proto, rhost=self.rhost, rport=str(self.rport))

@staticmethod
def _error(dh_error=None, code=500):
    """Keep 'login' happy and give some info back in case of failure"""
    return json.dumps({'result': False, 'error': {'code': code, 'message': str(dh_error)}})

def options(self):
    timeout = 10
    req = requests.Request('OPTIONS', 'rtsp://{host}:{port}?proto=Onvif RTSP/1.1\r\nCSeq: 1\r\n\r\n'.format(
        host='192.168.5.27', port=80))
    print(req.prepare())
    print(req.url)
    dh_data = self.remote.send(req.url, verify=False, allow_redirects=False, timeout=timeout)
    print(dh_data)

def post(self, url, query_args, timeout):
    """JSON API Communication"""
    return self.remote.post(self.uri + url, json=query_args, verify=False, allow_redirects=False, timeout=timeout)

def get(self, url, timeout):
    """Non JSON Communication"""
    return self.remote.get(self.uri + url, verify=False, allow_redirects=False, timeout=timeout)

def open_stream(self, session_id):
    """Open stream session for events and other 'client.Notify'"""
    self.stream = self.remote.get(
        '{}/SubscribeNotify.cgi?sessionId={}'.format(self.uri, session_id),
        verify=False, allow_redirects=False, stream=True
    )

def recv_stream(self):
    """Return events and other 'client.Notify'"""
    return fix_json(self.stream.raw.readline().decode('utf-8'))

@staticmethod
def can_recv():
    """We do not expect unexpected data
    the 'stream' above will handle that"""
    return False

@staticmethod
def connected():
    # TODO: Assume connected, should find a way to check
    return True

def close(self):
    # TODO: Not really sure if this way
    self.remote.close()
    return True
sagarspatil commented 3 years ago

@mcw0 You are a rockstar. We in this thread, owe you our firstborn.

mcw0 commented 3 years ago

@mcw0 You are a rockstar. We in this thread, owe you our firstborn. Thanks, but hell no, keep your shit for yourself! I already have two (what I know of) =]

Share is care, enjoy! )

sagarspatil commented 3 years ago

Now we wait for @rroller to work his magic :D

mcw0 commented 3 years ago

I can also see you are able to attach to the "Console", however you do not get any output from the "builtin Coonsole", only my stuff.

Sorry dude, I just now saw you are not able to attach to the "Console" ([-] Dahua JSON Console: Attach Console failed, using local only)

inevity commented 3 years ago

Have a dahua LECHENG(LC) camera, Device: IPC-A46L-LC, Web: 2.4, OEM: 000000 (From local console'device cmd) . This is the test result. Should i focus first which issue?

1. open port
PORT      STATE SERVICE
80/tcp    open  http
554/tcp   open  rtsp
8086/tcp  open  d-s-n
37777/tcp open  unknown
2.  can add this url  http://192.168.201.229:80/onvif/device_service to onvif device manger (ODM) to login in and live and stream video.  
3. forget the 4 test result. Now just can login success and fix the type error, now enter into the local console. But what is the local console? 

root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 37777 --proto dvrip --auth admin:Dxx (use correct password for 37777 port)

[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] UDP/TCP EventInOutServer listener thread: Started
[+] Opening connection to 192.168.201.229 on port 37777: Done
[+] Terminate Daemons thread: Started
[-] Dahua JSON Console: Attach Console failed, using local only
[+] Login: Success
[+] keepAlive thread: Started
[*] Remote Model: TP7S-4M, Class: IPC, Time: 2021-07-13 23:26:25
Traceback (most recent call last):
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 6720, in <module>
    status = DebugConsole()
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 740, in __init__
    self.MainConsole()
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 1299, in MainConsole
    if not self.ConnectRhost(
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 1490, in ConnectRhost
    if not dh.Connect():
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 2080, in Connect
    self.eventManager(msg='events 1')
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 5359, in eventManager
    self.eventManagerSetConfig(None)
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 5389, in eventManagerSetConfig
    self.InstanceService(methodName,start=True)
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 3110, in InstanceService
    if not self.CheckForService(methodName):
  File "/workspaces/Tools/Dahua-JSON-Debug-Console-v2.py", line 2875, in CheckForService
    if not len(self.RemoteServicesCache):
TypeError: object of type 'bool' has no len()
[*] Closed connection to 192.168.201.229 port 37777

4. Test result
root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --discover dhip
[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] DHDiscover response from: 192.168.201.229:37810
{
    "mac": "",
    "method": "client.notifyDevInfo",
    "params": {
        "deviceInfo": {
            "AlarmInputChannels": 0,
            "AlarmOutputChannels": 0,
            "DeviceClass": "IPC",
            "DeviceType": "TP7S-4M",
            "HttpPort": 80,
            "Port3": 80,
            "Port": 37777,
            "UnLoginFuncMask": 5,
            "IPv4Address": {
                "DefaultGateway": "192.168.201.1",
                "DhcpEnable": true,
                "IPAddress": "192.168.201.229",
                "SubnetMask": "255.255.255.0"
            },
            "MachineName": "",
            "Manufacturer": "Private",
            "RemoteVideoInputChannels": 0,
            "SerialNo": "",
            "Vendor": "LC",
            "Version": "2.800.0000000.5.R",
            "VideoInputChannels": 1,
            "VideoOutputChannels": 0,
            "Init": 11,
            "Find": "ACE",
            "FindVersion": 2
        }
    }
}
Note: dvrip proto discovery return nothing. 

root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 37777 --proto dhip --auth admin:L 
[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] UDP/TCP EventInOutServer listener thread: Started
[+] Opening connection to 192.168.201.229 on port 37777: Done
[+] Terminate Daemons thread: Started
[-] Dahua JSON Console: Failed
[-] Login: global.login [random]
[-] Timeout in P2P
[+] Successful instance termination of (null) (192.168.201.229)
[*] All done
[*] Closed connection to 192.168.201.229 port 37777
root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 37777 --proto dhip --auth admin:D
same timeout error as above , just use different password

root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 37777 --proto dvrip --auth admin:L
[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] UDP/TCP EventInOutServer listener thread: Started
[+] Opening connection to 192.168.201.229 on port 37777: Done
[+] Terminate Daemons thread: Started
[-] Dahua JSON Console: Failed
[-] Login: Authentication failed: 4 tries left 
[+] Successful instance termination of (null) (192.168.201.229)
[*] All done
[*] Closed connection to 192.168.201.229 port 37777
root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 37777 --proto dvrip --auth admin:D
same auth failure as above 

root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 80 --proto dhip --auth admin:D
[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] UDP/TCP EventInOutServer listener thread: Started
[+] Opening connection to 192.168.201.229 on port 80: Done
[+] Terminate Daemons thread: Started
[-] Dahua JSON Console: Failed
[-] Login: global.login: {'code': 268632085}
[+] Successful instance termination of (null) (192.168.201.229)
[*] All done
[*] Closed connection to 192.168.201.229 port 80
root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 80 --proto dhip --auth admin:L
same error code 268632085 as above 

root ➜ /workspaces/Tools (master ✗) $ python Dahua-JSON-Debug-Console-v2.py --rhost 192.168.201.229 --rport 37777 --proto dvrip --auth admin:L --debug
[*] [Dahua JSON Debug Console 2019-2021 bashis <mcw noemail eu>]
[+] UDP/TCP EventInOutServer listener thread: Started
[+] Opening connection to 192.168.201.229 on port 37777: Done
[+] Terminate Daemons thread: Started
[-] Dahua JSON Console: Failed
[-] Login: Authentication failed: 2 tries left 
[BEGIN SEND (192.168.201.229)] <------------------2259------------------>
a0010000|00000000|00000000|00000000|00000000|00000000|05020101|0000a1aa
[ END  SEND (192.168.201.229)] <------------------2259------------------>
[BEGIN RECV (192.168.201.229)] <------------------2379------------------>
b0000078|36000000|010e0100|00000000|00000000|01000000|0600f900|00000002

[ END  RECV (192.168.201.229)] <------------------2379------------------>
[BEGIN SEND (192.168.201.229)] <------------------2259------------------>
a0050000|47000000|00000000|00000000|00000000|00000000|05020008|0000a1aa
admin&&5A8C058D6C392862F5A4BD39511F904D3E2491EADC792A7936EFEB8C609A5826
[ END  SEND (192.168.201.229)] <------------------2259------------------>
[BEGIN RECV (192.168.201.229)] <------------------2365------------------>
b0000078|00000000|01000100|00000000|00000000|01000000|0600f900|00020002
[ END  RECV (192.168.201.229)] <------------------2365------------------>
[+] Successful instance termination of (null) (192.168.201.229)
[*] All done
[*] Closed connection to 192.168.201.229 port 37777
mcw0 commented 3 years ago

TypeError: object of type 'bool' has no len()

Interesting, FYI, you need to have valid username/password to get my script to work, what I could see from your dumps, you don't except this, and there is a crash in the script as I am parsing things wrongly, do not belong to @rroller work. Please report to my stuff here

inevity commented 3 years ago

you need to have valid username/password to get my script to work,

Now once fix the no len() error, can enter into the local console. so what is next job to do to hack?

mcw0 commented 3 years ago

Now once fix the no len() error, can enter into the local console. so what is next job to do to hack?

With the message in your first attempt: "Dahua JSON Console: Attach Console failed, using local only" Same issue as with NVR's, the "console" are not available... unfortunately.

inevity commented 3 years ago

Same issue as with NVR's, the "console" are not available... unfortunately.

So next should we focus the socket layer to reverse?

sagarspatil commented 3 years ago

@rroller Any update on this? Is there any way we can help?

sagarspatil commented 3 years ago

Port 80. DHIP.

Cool!

DHIP working, so HTTP/HTTPS with JSON should work just fine as well.

Below you find the raw HTTP/HTTPS part of my next script. Trying/working on to split up stuff to get better overview and functionality. Beware, will not just work out of the box, just to give some ideas before I am done with other parts of my script.

@rroller @sagarspatil @gelokatil (et all) Hope it will help you out in someway...

from requests import packages from requests.packages import urllib3 from requests.packages.urllib3 import exceptions

class DahuaHttp(object): def del(self): log.info('DahuaHttp DELETE')

""" Dahua http """
def __init__(self, rhost, rport, proto, timeout=60):
    super(DahuaHttp, self).__init__()

    self.rhost = rhost
    self.rport = rport
    self.proto = proto
    self.timeout = timeout

    self.remote = None
    self.uri = None
    self.stream = None

    """ Most devices will use self-signed certificates, suppress any warnings """
    requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

    self.remote = requests.Session()

    """Used with _debug"""
    self.headers = self.remote.headers
    self.cookies = self.remote.cookies

    self._init_uri()

    import random as random_agent
    random_agent.seed(1)
    self.remote.headers.update({
        'User-Agent': useragents.random(),
        'X-Requested-With': 'XMLHttpRequest',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Language': 'en-US,en;q=0.9',
        'Host': '{}:{}'.format(self.rhost, self.rport),
    })
    """
    self.remote.proxies.update({
        # 'http': 'http://127.0.0.1:8080',
    })
    """

def send(self, url=None, query_args=None, login=False, timeout=5):

    """JSON API communication"""
    if query_args:
        if query_args.get('params') is not None and not len(query_args.get('params')):
            query_args.update({'params': None})

    """This weird code will try automatically switch between http/https
    and update Host
    """
    try:
        if url and not query_args:
            return self.get(url, timeout)
        else:
            dh_data = self.post(self._get_url(login, url), query_args, timeout)
    except requests.exceptions.ConnectionError:
        self.proto = 'https' if self.proto == 'http' else 'https'
        self._init_uri()
        try:
            if url and not query_args:
                return self.get(url, timeout)
            else:
                dh_data = self.post(self._get_url(login, url), query_args, timeout)
        except requests.exceptions.ConnectionError as e:
            if login:
                return self._error(dh_error=e)
            return None
    except requests.exceptions.RequestException as e:
        if login:
            return self._error(dh_error=e)
        return None
    except KeyboardInterrupt:
        return None

    """302 when requesting http on https enabled device"""
    if dh_data.status_code == 302:
        redirect = dh_data.headers.get('Location')
        self.uri = redirect[:redirect.rfind('/')]
        self._update_host()
        if url and not query_args:
            return self.get(url, timeout)
        else:
            dh_data = self.post(self._get_url(login, url), query_args, timeout)

    """Catch non dahua hosts"""
    if not dh_data.status_code == 200:
        return self._error(dh_error=dh_data.text, code=dh_data.status_code)

    """JSON API communication"""
    dh_json = dh_data.json()

    """Set SessionID Cookie during login"""
    if login and self.remote.cookies.get('DWebClientSessionID') is None:
        self.remote.cookies.set('username', query_args.get('params').get('userName'))
        self.remote.cookies.set('DWebClientSessionID', dh_json.get('session'))

    return dh_data

@staticmethod
def _get_url(login, url):
    if login:
        return '/RPC2_Login'
    elif url:
        """GET or other POST JSON API communication"""
        return url
    """Default JSON API communication"""
    return '/RPC2'

def _update_host(self):
    if not self.remote.headers.get('Host') == self.uri[self.uri.rfind('://') + 3:]:
        self.remote.headers.update({
            'Host': self.uri[self.uri.rfind('://') + 3:],
        })

def _init_uri(self):
    self.uri = '{proto}://{rhost}:{rport}'.format(proto=self.proto, rhost=self.rhost, rport=str(self.rport))

@staticmethod
def _error(dh_error=None, code=500):
    """Keep 'login' happy and give some info back in case of failure"""
    return json.dumps({'result': False, 'error': {'code': code, 'message': str(dh_error)}})

def options(self):
    timeout = 10
    req = requests.Request('OPTIONS', 'rtsp://{host}:{port}?proto=Onvif RTSP/1.1\r\nCSeq: 1\r\n\r\n'.format(
        host='192.168.5.27', port=80))
    print(req.prepare())
    print(req.url)
    dh_data = self.remote.send(req.url, verify=False, allow_redirects=False, timeout=timeout)
    print(dh_data)

def post(self, url, query_args, timeout):
    """JSON API Communication"""
    return self.remote.post(self.uri + url, json=query_args, verify=False, allow_redirects=False, timeout=timeout)

def get(self, url, timeout):
    """Non JSON Communication"""
    return self.remote.get(self.uri + url, verify=False, allow_redirects=False, timeout=timeout)

def open_stream(self, session_id):
    """Open stream session for events and other 'client.Notify'"""
    self.stream = self.remote.get(
        '{}/SubscribeNotify.cgi?sessionId={}'.format(self.uri, session_id),
        verify=False, allow_redirects=False, stream=True
    )

def recv_stream(self):
    """Return events and other 'client.Notify'"""
    return fix_json(self.stream.raw.readline().decode('utf-8'))

@staticmethod
def can_recv():
    """We do not expect unexpected data
    the 'stream' above will handle that"""
    return False

@staticmethod
def connected():
    # TODO: Assume connected, should find a way to check
    return True

def close(self):
    # TODO: Not really sure if this way
    self.remote.close()
    return True

@mcw0 Have you by any chance published an updated script that will help @rroller build support for Imou Ranger 2 cameras?

rroller commented 3 years ago

@sagarspatil no I haven't had any time to work on this lately. Once I get free time I will

sagarspatil commented 3 years ago

@sagarspatil no I haven't had any time to work on this lately. Once I get free time I will

No worries. Thank you for taking the time to respond.

mcw0 commented 3 years ago

@mcw0 Have you by any chance published an updated script that will help @rroller build support for Imou Ranger 2 cameras?

No, sorry, currently on holiday, I'm not happy yet with my new rewrite yet and I also expect to release two 0day in September/October that should be included in the next version of the script.

umutcelebi commented 3 years ago

Hi guys, I have a Imou ranger 2 4MP (Other name of device: Imou A1 4MP). Can i help and check things out?

wolter10 commented 3 years ago

I presume the code that is in the works will also apply for the Imou Bullet 2E (IPC-F42FP)? This crappy software is a real bummer, after my great experiences with the VTO 2202 doorbell from Dahua it feels like this software and code is in every way inferior.

sagarspatil commented 3 years ago

@rroller Any update on this kind sir

rroller commented 3 years ago

No I'm very sorry. It's not an easy problem to solve and I don't have much time right now.

sagarspatil commented 3 years ago

No I'm very sorry. It's not an easy problem to solve and I don't have much time right now.

No worries. Thanks for responding. I appreciate it.

semihselcuk commented 2 years ago

I am also looking forward to this implementation. Happy to be beta tester as well. Thanks everyone for working on this.

mcw0 commented 2 years ago

@rroller FYI, I published the new script here

Not very sure it will help you anyhow, I've started to suspect some time ago that this "iMou Ranger 2" has limited set of the full Dahua JSON...

Anyway, just wanted to give you heads-up.

sagarspatil commented 2 years ago

@rroller Do you have any plans to support this camera in future? If not, could you let us know, I think, in that case, I’ll just upgrade my cameras.

rossiluis22 commented 2 years ago

I’m also waiting for this integration but I understand that having free time is not easy, and I don’t have the knowledge to do it, So I think I’ll ended up buying one of cameras that are already integrated

geyles commented 2 years ago

I too would love integration for the ranger 2

cpanel10x commented 2 years ago

Same with IMOU F22FP. Any new temporary solution?

PrathikGopal commented 2 years ago

Thank you for the hard work on this guys, I see the same issue on an imou IPC- F22AP. Hoping to see through this one !👍🏽

https://www.imoulife.com/public/asset/files/QSG-IPC-F22A_(PoE).pdf

vuisme commented 2 years ago

After all, i dont know how to use wrapper... Need a guide on read me ._.

piggei commented 2 years ago

If it can help:

IMOU CUE 2C

$ nmap 192.168.1.120 Starting Nmap 7.80 ( https://nmap.org ) at 2022-01-18 23:41 CET Nmap scan report for NOMI-IPC-C22C.station (192.168.1.120) Host is up (0.0052s latency). Not shown: 997 closed ports PORT STATE SERVICE 80/tcp open http 554/tcp open rtsp 8086/tcp open d-s-n

./Console.py --auth admin:xxxxx --rhost 192.168.1.120 --proto dvrip --rport 80 [!] Pwntools does not support 32-bit Python. Use a 64-bit release. [] [Dahua Debug Console 2019-2021 bashis ] [] logon type "default" with proto "dvrip" at 192.168.1.120:80 [+] Opening connection to 192.168.1.120 on port 80: Done [-] Dahua Debug Console: Attach Console failed, using local only [+] Login: Success [+] keepAlive thread: Started [] [Active Users] admin@192.168.1.14 since 2022-01-18 23:29:45 with "DVRIP" (Id: 1) admin@127.0.0.1 since 2022-01-18 22:28:02 with "DVRIP" (Id: 2) [] Remote Model: IPC-C22C, Class: IPC, Time: 2022-01-18 23:29:45

[Console]# device [+] [System] Vendor: Lechange, Build: 2021-12-18 16:35:21, Version: 2.680.0000000.22.R Device: IPC-C22C, Web: 2.4, OEM: 0000000 Package: (null) [+] [Encrypt Info] Asymmetric: RSA, Cipher: AES; RPAC, Padding: (null), RSA Exp.: 010001 RSA Modulus: [ ... ] -----BEGIN PUBLIC KEY----- [ ... ] -----END PUBLIC KEY----- [Console]# console [*] Console: dh0, Device: IPC-C22C (192.168.1.120) Active [Console]# ldiscover dhip 192.168.1.120 [+] dh_discover response from: 192.168.1.120:37810 { "mac": "b4:4c:3b:90:f7:f4", "method": "client.notifyDevInfo", "params": { "deviceInfo": { "AlarmInputChannels": 0, "AlarmOutputChannels": 0, "DeviceClass": "IPC", "DeviceType": "IPC-C22C", "HttpPort": 80, "Port3": 80, "Port": 37777, "UnLoginFuncMask": 1, "IPv4Address": { "DefaultGateway": "192.168.1.1", "DhcpEnable": true, "IPAddress": "192.168.1.120", "SubnetMask": "255.255.255.0" }, "MachineName": "7J052D0PAZEAB79", "Manufacturer": "Private", "RemoteVideoInputChannels": 0, "SerialNo": "7J052D0PAZEAB33", "DeviceID": "", "Vendor": "Lechange", "Version": "2.680.0000000.22.R", "VideoInputChannels": 1, "VideoOutputChannels": 0, "Init": 1158, "Find": "BC", "FindVersion": 1, "CountryCode": "DE" } } }

It works also with DHIP protocol, no access to 37777 port.

Same parameters (ip, login, pw, 80, 544) to DAHUA integration: Username, Password, or Address is wrong.

Error in HA log: 2022-01-18 23:28:59 ERROR (MainThread) [custom_components.dahua] TimeoutError fetching information from http://192.168.1.120:80/cgi-bin/magicBox.cgi?action=getMachineName - 2022-01-18 23:29:09 ERROR (MainThread) [custom_components.dahua] TimeoutError fetching information from http://192.168.1.120:80/cgi-bin/magicBox.cgi?action=getSystemInfo - 2022-01-18 23:44:16 ERROR (MainThread) [custom_components.dahua] Could not connect to Dahua device. For iMou devices see https://github.com/rroller/dahua/issues/6 Traceback (most recent call last): File "/home/homeassistant/.homeassistant/custom_components/dahua/config_flow.py", line 179, in _test_credentials data.update(serial) AttributeError: 'NoneType' object has no attribute 'update'

wwwuser83 commented 2 years ago

Please, if you can - add support for IMOU IPC-F22 Bullet 2C (router see it like NOMI-IPC-F22; ONVIF integration also writes it like IPC-F22 Lechange, it works but there are no alerts - only image). DAHUA integration via HACS responds with "Username, Password, or Address is wrong" (but it is ok - checked + works with ONVIF) Current cam firmware: 2.680.0000000.22.R 2021-12-18

loples commented 2 years ago

There are news, on implementation of Imou Ranger 2 IPC-A22E?

mspbonn commented 2 years ago

My attempt connection IMOU Cruiser 4MP:

$ python3 Console.py --auth admin:XXXXXXXX --rhost 192.168.179.ZZZ --proto dvrip --rport 80 /usr/local/lib/python3.5/dist-packages/OpenSSL/crypto.py:14: CryptographyDeprecationWarning: Python 3.5 support will be dropped in the next release of cryptography. Please upgrade your Python. from cryptography import utils, x509 [] [Dahua Debug Console 2019-2021 bashis ] [] logon type "default" with proto "dvrip" at 192.168.179.ZZZ:80 [+] Opening connection to 192.168.179.ZZZ on port 80: Done [-] Dahua Debug Console: Failed [-] Login: Realm [p2p] while len(dh_data) TypeError("the JSON object must be str, not 'bytes'",) b'Realm:Login to YYYYYYYYYYYYYYYY\r\nRandom:1386202155d\r\n\r\n' [] All done [] Closed connection to 192.168.179.ZZZ port 80

$ nmap 192.168.179.ZZZ

Starting Nmap 7.40 ( https://nmap.org ) at 2022-02-17 20:46 CET Nmap scan report for 192.168.179.ZZZ Host is up (0.0039s latency). Not shown: 996 closed ports PORT STATE SERVICE 53/tcp filtered domain 80/tcp open http 554/tcp open rtsp 8086/tcp open d-s-n

Nmap done: 1 IP address (1 host up) scanned in 22.73 seconds

On port 8086 I also get no response.

mspbonn commented 2 years ago

I've installed ispyagentdvr as docker container:

https://hub.docker.com/r/doitandbedone/ispyagentdvr

The IMOU Cruiser isn't listed, but works as 'Cue 2' without any problem. What I need is a simple short code to move the cameras from one preset position to another. The recording will be done by motion.

Whastie commented 2 years ago

Hello

Do you have any new news about the imou Cruiser 4MP and the ranger 2 4Mp

thank you all very much for helping!!!

mspbonn commented 2 years ago

I have sent back the camera to the seller, because the Cruiser doesn't support the Move- and Goto-commands. Now I use a D-Link DCS-8635LH which supports the commands. I move the camera from position to position by using _onvifcli (https://github.com/quatanium/python-onvif). The problem with many cameras seems to be, that most of them doesn't support the Move- and Goto-commands - even if they are ONVIF Profile "S" or "T"-Type cameras, because these commands are not mandantory.

engrumair commented 2 years ago

I have an IMOU camera Model: IPC-F42 (4mega pixel). it has the same problem. The user name, password, or Ip address is wrong. Here is the log: On the other hand Imou camera model: IPC-C26E-V2 is working fine..

2022-04-23 12:24:54 ERROR (MainThread) [custom_components.dahua] Could not connect to Dahua device. For iMou devices see https://github.com/rroller/dahua/issues/6 Traceback (most recent call last): File "/config/custom_components/dahua/config_flow.py", line 177, in _test_credentials data = await client.get_machine_name() File "/config/custom_components/dahua/client.py", line 101, in get_machine_name return await self.get("/cgi-bin/magicBox.cgi?action=getMachineName") File "/config/custom_components/dahua/client.py", line 687, in get raise exception File "/config/custom_components/dahua/client.py", line 669, in get response = await auth.request("GET", url) File "/config/custom_components/dahua/digest.py", line 46, in request response = await self.session.request(method, url, headers=headers, **kwargs) File "/usr/local/lib/python3.9/site-packages/aiohttp/client.py", line 559, in _request await resp.start(conn) File "/usr/local/lib/python3.9/site-packages/aiohttp/client_reqrep.py", line 898, in start message, payload = await protocol.read() # type: ignore[union-attr] File "/usr/local/lib/python3.9/site-packages/aiohttp/streams.py", line 616, in read await self._waiter aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected

laurent-pereira commented 2 years ago

Hi,

Any update about Imou Ranger 2 IPC-A22E integration ?

VaLeXaR commented 2 years ago

Hi @rroller. I fixed RPC2 class for authentification and updating session. I needed to use coaxial control that wasn't working through cgi on my iMou IPC-T26EP. Maybe you want to change cgi client to RPC2 in the future. https://github.com/VaLeXaR/dahua/tree/coaxial_control

array81 commented 2 years ago

I understand that IMOU camera support hasn't been implemented yet, right? I have a Cue 2C (‎IPC-C22CP) and a Cue2 (‎IPC-C22E) and I have problem with these:

Any news about IMOU support?

andregoncalvespires commented 2 years ago

Imou db60 doorbell?

alcool179 commented 2 years ago

Hi, anyone knows how to grab a jpg from Imou Cue 2C (‎IPC-C22CP) Camera has open port 554 for rtsp stream and also port 80 open ... but for what?! I tried all jpeg path from https://www.ispyconnect.com/camera/imou but none works

THX

sophof commented 2 years ago

So do I understand correctly now that this feature will never come? I just bought two imou Rex devices (IPC-A46LP-D) but it has become clear that without this feature I'll have to return them, sadly they are not as 'onvif-ready' as implied...

rroller commented 2 years ago

@sophof I'm very sorry that I'm short on time and can't dedicate time to get this working. Right now I won't have the time, maybe someone else will jump in and help out but not sure, it's not an easy problem to solve. But I think @VaLeXaR already did the actual hard work, so we need to integrate that.

sophof commented 2 years ago

@rroller Sorry, I see now that my comment was a bit harsh, it wasn't intended that way. It simply was unclear to me what the status was in general. Since I just bought them I still have a window where I can return them, so I really wanted it to be clear, that was all :) I only know too well what it is like to want to work on these things but lack the time to put in the effort.

I have some experience working on home assistant integrations, but none on camera's and their API's but maybe I can help @VaLeXaR (under similar time constraints :P)? I had a look at your code a few days ago, but I discounted it because it was about coaxial control, so I assumed it wasn't relevant?

sophof commented 2 years ago

After reading some more I think I understand, let me try to summarise. These cameras exclusively use the RPC2 API, where the current integration uses a REST api, which works for most older cameras. For coaxial control @VaLeXaR needed to use the RPC2 api, so he further adjusted that for his usecase. However, all currently supported camera's, including those via the 'current' rpc2 api communicate through HTTP/HTTPS, where the imou camera's for some reason only use something called DHIP. It stands to reason that they haven't written an entirely new API, so probably what is needed is some way to 'translate' (my knowledge isn't deep enough to understand what is needed for that).

I see @mcw0 is working on a dahua console that supports DHIP and is in pre-alpha status though. Presumably this is the same work in essence?

mcw0 commented 2 years ago

@sophof DHIP is the way to go, working both on HTTP/HTTPS and binary mode. CGI stuff totally sux, just lacking lots of stuff. I did some attempts for making it as python library, but I'm far from done, as I have other things to do. However, if @rroller , @sophof or anyone else are interested to work with that as well, I can setup a repo for that.

sophof commented 2 years ago

@mcw0 Yeah, I saw that your code was currently at the 'pre-alpha' stage. Sadly, my level of knowledge about these communication protocols is literally zero, so it would require a lot of effort for likely little result from my part. I have bought a camera from TP-link that looks similar to test as an alternative. That has working local control in home assistant as far as I can tell, so once it arrives and I'm happy I'll likely take the easy road and just return the imou cameras.

[edit] In the end I decided to keep the cameras since other alternatives (available here at least) all had other disadvantages. So I'm still interested but also still lacking in skills :P

rogodra commented 1 year ago

Hi @rroller. I fixed RPC2 class for authentification and updating session. I needed to use coaxial control that wasn't working through cgi on my iMou IPC-T26EP. Maybe you want to change cgi client to RPC2 in the future. https://github.com/VaLeXaR/dahua/tree/coaxial_control

With this 'coaxial control' variant, I can enter the Imou IPC-A22E. Also the camera view works, the other entities don't seem to work. Thanks for the work!