From 6a6890ef94adab5655d91d58b95d8642145f92c0 Mon Sep 17 00:00:00 2001 From: myhomeiot <70070601+myhomeiot@users.noreply.github.com> Date: Mon, 13 Sep 2021 13:49:19 +0300 Subject: [PATCH] Added send_instance_command service, refactored open_door service, now it's internally call send_instance_command Updated documentation Update manifest.json Update sensor.py --- README.md | 19 +++- custom_components/dahua_vto/manifest.json | 2 +- custom_components/dahua_vto/sensor.py | 107 +++++++++++++++------ custom_components/dahua_vto/services.yaml | 109 +++++++++++++--------- 4 files changed, 161 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 5c07125..c6309d0 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,8 @@ lock: # Commands and Events -You can send any command using the service `dahua_vto.send_command` and receive reply as event. +You can send any command using the service `dahua_vto.send_command` or `dahua_vto.send_instance_command` and receive reply as event. +`dahua_vto.send_instance_command` sequential call to service.factory.instance, service.method with object returned by factory.instance and service.destroy, where service it's a first part of the `method` before `.`. Result of service.method returned as event. I doesn't found documentation but you can grab some commands and their parameters from [Dahua-JSON-Debug-Console-v2.py](https://github.com/mcw0/Tools) All device `client.notifyEventStream` messages you will receive as events, information about some of them you can find [here](https://github.com/elad-bar/DahuaVTO2MQTT/blob/master/MQTTEvents.MD). @@ -205,6 +206,22 @@ data: entity_id: sensor.dahua_vto method: magicBox.getBootParameter params: {names: ['serverip', 'ver']} + +# Cancel VTO Call +service: dahua_vto.send_command +data: + entity_id: sensor.dahua_vto + method: console.runCmd + params: {'command': 'hc'} + event: false + +# Delete VTH Records +service: dahua_vto.send_instance_command +data: + entity_id: sensor.dahua_vth + method: RecordUpdater.clear + instance_params: {'name': 'VideoTalkMissedLog'} + event: false ``` # Debugging diff --git a/custom_components/dahua_vto/manifest.json b/custom_components/dahua_vto/manifest.json index 4133b2e..6f995e0 100644 --- a/custom_components/dahua_vto/manifest.json +++ b/custom_components/dahua_vto/manifest.json @@ -1,7 +1,7 @@ { "domain": "dahua_vto", "name": "Dahua VTO", - "version": "1.0.5", + "version": "1.0.6", "documentation": "https://github.com/myhomeiot/DahuaVTO", "issue_tracker": "https://github.com/myhomeiot/DahuaVTO/issues", "requirements": [], diff --git a/custom_components/dahua_vto/sensor.py b/custom_components/dahua_vto/sensor.py index 0c16c11..cde7031 100644 --- a/custom_components/dahua_vto/sensor.py +++ b/custom_components/dahua_vto/sensor.py @@ -43,26 +43,30 @@ CONF_CHANNEL = "channel" CONF_SHORT_NUMBER = "short_number" CONF_METHOD = "method" +CONF_INSTANCE_PARAMS = "instance_params" CONF_PARAMS = "params" CONF_TAG = "tag" SERVICE_DEFAULT_TIMEOUT = 5 -SERVICE_OPEN_DOOR = "open_door" -SERVICE_OPEN_DOOR_SCHEMA = vol.Schema( +SERVICE_SEND_COMMAND = "send_command" +SERVICE_SEND_COMMAND_SCHEMA = vol.Schema( { vol.Required(CONF_ENTITY_ID): cv.string, - vol.Required(CONF_CHANNEL): int, - vol.Optional(CONF_SHORT_NUMBER, default="HA"): cv.string, + vol.Required(CONF_METHOD): object, + vol.Optional(CONF_PARAMS, default=None): object, + vol.Optional(CONF_EVENT, default=True): bool, + vol.Optional(CONF_TAG, default=None): object, vol.Optional(CONF_TIMEOUT, default=SERVICE_DEFAULT_TIMEOUT): int, } ) -SERVICE_SEND_COMMAND = "send_command" -SERVICE_SEND_COMMAND_SCHEMA = vol.Schema( +SERVICE_SEND_INSTANCE_COMMAND = "send_instance_command" +SERVICE_SEND_INSTANCE_COMMAND_SCHEMA = vol.Schema( { vol.Required(CONF_ENTITY_ID): cv.string, vol.Required(CONF_METHOD): object, + vol.Optional(CONF_INSTANCE_PARAMS, default=None): object, vol.Optional(CONF_PARAMS, default=None): object, vol.Optional(CONF_EVENT, default=True): bool, vol.Optional(CONF_TAG, default=None): object, @@ -70,6 +74,16 @@ } ) +SERVICE_OPEN_DOOR = "open_door" +SERVICE_OPEN_DOOR_SCHEMA = vol.Schema( + { + vol.Required(CONF_ENTITY_ID): cv.string, + vol.Required(CONF_CHANNEL): int, + vol.Optional(CONF_SHORT_NUMBER, default="HA"): cv.string, + vol.Optional(CONF_TIMEOUT, default=SERVICE_DEFAULT_TIMEOUT): int, + } +) + async def async_setup_platform( hass, config, add_entities, discovery_info=None @@ -82,16 +96,21 @@ async def async_setup_platform( hass.loop.create_task(entity.async_run()) platform = entity_platform.async_get_current_platform() - platform.async_register_entity_service( - SERVICE_OPEN_DOOR, - SERVICE_OPEN_DOOR_SCHEMA, - "async_open_door" - ) platform.async_register_entity_service( SERVICE_SEND_COMMAND, SERVICE_SEND_COMMAND_SCHEMA, "async_send_command" ) + platform.async_register_entity_service( + SERVICE_SEND_INSTANCE_COMMAND, + SERVICE_SEND_INSTANCE_COMMAND_SCHEMA, + "async_send_instance_command" + ) + platform.async_register_entity_service( + SERVICE_OPEN_DOOR, + SERVICE_OPEN_DOOR_SCHEMA, + "async_open_door" + ) return True @@ -214,19 +233,6 @@ async def command(self, message, timeout=5): finally: self.on_response = self.on_response_id = None - async def open_door(self, channel, short_number, timeout): - object_id = (await self.command({ - "method": "accessControl.factory.instance", - "params": {"channel": channel}}, timeout))["result"] - if object_id: - try: - await self.command({ - "method": "accessControl.openDoor", "object": object_id, - "params": {"DoorIndex": 0, "ShortNumber": short_number}}) - finally: - await self.command({ - "method": "accessControl.destroy", "object": object_id}) - async def send_command(self, method, params, event, tag, timeout): if isinstance(method, dict): message = method @@ -244,6 +250,29 @@ async def send_command(self, method, params, event, tag, timeout): result["entity_id"] = self.entity.entity_id self.hass.bus.fire(DOMAIN, result) + async def send_instance_command( + self, method, instance_params, params, event, tag, timeout + ): + service = method.partition('.')[0] + object_id = (await self.command({ + "method": f"{service}.factory.instance", + "params": instance_params}, timeout))["result"] + if object_id: + try: + result = await self.command({ + "method": method, "object": object_id, "params": params}) + if event: + del result["id"] + del result["session"] + result["method"] = method + if tag: + result["tag"] = tag + result["entity_id"] = self.entity.entity_id + self.hass.bus.fire(DOMAIN, result) + finally: + await self.command({ + "method": f"{service}.destroy", "object": object_id}) + async def heartbeat_loop(self): result = await self.command({"method": "magicBox.getSystemInfo"}) if result.get("result"): @@ -327,20 +356,38 @@ def state_attributes(self): def update(self): self._state = 'OK' if self.protocol is not None else None - async def async_open_door(self, channel, short_number, timeout) -> None: + async def async_send_command( + self, method, params, event, tag, timeout + ) -> None: if self.protocol is None: raise HomeAssistantError("not connected") try: - await self.protocol.open_door(channel - 1, short_number, timeout) + await self.protocol.send_command( + method, params, event, tag, timeout + ) except asyncio.TimeoutError: raise HomeAssistantError("timeout") - async def async_send_command(self, method, params, - event, tag, timeout) -> None: + async def async_send_instance_command( + self, method, instance_params, params, event, tag, timeout + ) -> None: + if self.protocol is None: + raise HomeAssistantError("not connected") + try: + await self.protocol.send_instance_command( + method, instance_params, params, event, tag, timeout + ) + except asyncio.TimeoutError: + raise HomeAssistantError("timeout") + + async def async_open_door(self, channel, short_number, timeout) -> None: if self.protocol is None: raise HomeAssistantError("not connected") try: - await self.protocol.send_command(method, params, - event, tag, timeout) + await self.protocol.send_instance_command( + "accessControl.openDoor", {"channel": channel - 1}, + {"DoorIndex": 0, "ShortNumber": short_number}, + None, None, timeout + ) except asyncio.TimeoutError: raise HomeAssistantError("timeout") diff --git a/custom_components/dahua_vto/services.yaml b/custom_components/dahua_vto/services.yaml index 0553b5e..ddfda15 100644 --- a/custom_components/dahua_vto/services.yaml +++ b/custom_components/dahua_vto/services.yaml @@ -1,6 +1,6 @@ -open_door: - name: Open door - description: Open the door +send_command: + name: Send command + description: Send the command fields: <<: &entity_id entity_id: @@ -11,22 +11,36 @@ open_door: selector: entity: domain: sensor - channel: - name: Channel - description: Number of channel starting from 1 - required: true - example: 1 - selector: - number: - min: 1 - max: 15 - short_number: - name: Short Number - description: Short number to show in the log as Room No. - default: HA - example: HA - selector: - text: + <<: &method + method: + name: Method + description: "Method name, example: magicBox.getBootParameter" + required: true + example: system.listService + selector: + object: + <<: ¶ms + params: + name: Params + description: "Method parameters, example: {names: ['serverip', 'ver']}" + example: "{names: ['serverip', 'ver']}" + selector: + object: + <<: &event + event: + name: Event + description: Fire event with result + default: True + example: True + selector: + boolean: + <<: &tag + tag: + name: Tag + description: "Tag, will be present in event data, example: 1 or {name: tag}" + example: "{name: tag}" + selector: + object: <<: &timeout timeout: name: Timeout @@ -38,35 +52,42 @@ open_door: min: 1 max: 99 -send_command: - name: Send command - description: Send the command +send_instance_command: + name: Send instance command + description: Send the command to the instance, sequential call to service.factory.instance, service.method with object returned by factory.instance, service.destroy fields: <<: *entity_id - method: - name: Method - description: "Method name, example: magicBox.getBootParameter" - required: true - example: system.listService + <<: *method + instance_params: + name: Instance params + description: "Instance method parameters, for service.factory.instance call, example: {'name': 'VideoTalkMissedLog'}" + example: "{'name': 'VideoTalkMissedLog'}" selector: object: - params: - name: Params - description: "Method parameters, example: {names: ['serverip', 'ver']}" - example: "{names: ['serverip', 'ver']}" - selector: - object: - event: - name: Event - description: Fire event with result - default: True - example: True + <<: *params + <<: *event + <<: *tag + <<: *timeout + +open_door: + name: Open door + description: Open the door + fields: + <<: *entity_id + channel: + name: Channel + description: Number of channel starting from 1 + required: true + example: 1 selector: - boolean: - tag: - name: Tag - description: "Tag, will be present in event data, example: 1 or {name: tag}" - example: "{name: tag}" + number: + min: 1 + max: 15 + short_number: + name: Short number + description: Short number to show in the log as Room No. + default: HA + example: HA selector: - object: + text: <<: *timeout