jasonacox / tinytuya

Python API for Tuya WiFi smart devices using a direct local area network (LAN) connection or the cloud (TuyaCloud API).
MIT License
1.03k stars 182 forks source link

DoorbellDevice works only when device is online #162

Open jonesMeUp opened 2 years ago

jonesMeUp commented 2 years ago

moin, i didn't make a pull request, because the doorbelldevice works only if the device is online and therefore its useless (at least for me). but i said i will make a doorbelldevice - so here it is: DoorbellDevice.zip its a gift for jasonacox, do with it whatever you want... i give all the rights to you.

# TinyTuya Doorbell Device
# -*- coding: utf-8 -*-
"""
 Python module to interface with Tuya WiFi smart devices

 Author: JonesMeUp
 Tested: LSC-Bell 8S(AKV300_8M)
 Note: Without hack the device can't be used offline. 
       With hack the DoorbellDevice is useless.

 For more information see https://github.com/jasonacox/tinytuya

 Local Control Classes
    DoorbellDevice(dev_id, address, local_key=None, dev_type='default')

 Functions
    DoorbellDevice:
        set_basic_indicator(bool):
        set_volume(1-10):
        set_motion_area(x,y,lenX, lenY)
        set_motion_area_switch(bool)
"""

from .core import Device

class DoorbellDevice(Device):
    """
    Represents a Tuya based Video-Doorbell.

    Args:
        dev_id (str): The device id.
        address (str): The network address.
        local_key (str, optional): The encryption key. Defaults to None.
    """
    DPS_2_STATE = {
        "101": "basic_indicator",     # Boolean                                                (status indicator)
        "103": "basic_flip",          # Boolean                                                (flip video vertically)
        "104": "basic_osd",           # Boolean                                                (timestap on video)
        "106": "motion_sensitivity",  # Enum ["0","1","2"]                                     (low, medium, high)
        "108": "basic_nightvision",   # Enum ["0","1","2"]                                     (auto, off, on)
        "109": "sd_storge",           # String ["maxlen":255]                                  (capacity|used|free) e.g: '258048|50176|207872'
        "110": "sd_status",           # Integer ["min":1,"max": 5,"scale":1,"step":1]
        "111": "sd_format",           # Boolean
        "115": "movement_detect_pic", # Raw
        "117": "sd_format_state",     # Integer ["min":-20000,"max":20000,"scale":1,"step":1]
        "134": "motion_switch",       # Boolean                                               (alarm on motion detection)
        "136": "doorbell_active",     # String ["maxlen":255]                                 (doorbell was pressed)
        "150": "record_switch",       # Boolean                                               (false = no recording)
        "151": "record_mode",         # Enum ["1","2"]                                        (1=on event, 2=always)
        "154": "doorbell_pic",        # Raw                                                   (picture of the device)
        "155": "doorbell_ring_exist", # Enum ["0","1"]
        "156": "chime_ring_tune",     # Enum ["1","2","3","4"]
        "157": "chime_ring_volume",   # Integer ["min":0,"max":100,"scale":1,"step":1]        (chime is an extrenal gong [433MhZ])
        "160": "basic_device_volume", # Integer ["min":1,"max": 10,"scale":0,"step":1]
        "165": "chime_settings",      # Enum ["0","2","3"]
        "168": "motion_area_switch",  # Boolean                                               (false = use full area)
        "169": "motion_area",         # String ["maxlen":255]                                 (x, y, xlen, ylen)
        "185": "alarm_message",       # String
    }
    DPS_2_FUNC = {
        "101": "basic_indicator",     # Boolean
        "103": "basic_flip",          # Boolean
        "104": "basic_osd",           # Boolean
        "106": "motion_sensitivity",  # Enum ["0","1","2"]
        "108": "basic_nightvision",   # Enum ["0","1","2"]
        "111": "sd_format",           # Boolean
        "134": "motion_switch",       # Boolean
        "150": "record_switch",       # Boolean
        "151": "record_mode",         # Enum ["1","2"]
        "155": "doorbell_ring_exist", # Enum ["0","1"]
        "156": "chime_ring_tune",     # Enum ["1","2","3","4"]
        "157": "chime_ring_volume",   # Integer ["min":0,"max":100,"scale":1,"step":1]
        "160": "basic_device_volume", # Integer ["min":1,"max": 10,"scale":0,"step":1]
        "165": "chime_settings",      # Enum ["0","2","3"]
        "168": "motion_area_switch",  # Boolean
        "169": "motion_area",         # String ["maxlen":255]
    }

    def __init__(self, dev_id, address, local_key="", dev_type="default"):
        super(DoorbellDevice, self).__init__(dev_id, address, local_key, dev_type)

    def set_basic_indicator(self, val=True, nowait=False):
        """ Set the basic incicator """
        self.set_value(101, bool(val), nowait)

    def set_volume(self, vol=10, nowait=False):
        """ Set the doorbell volume """
        if vol < 3:
            vol = 3 # Nothing to hear below 3
        if vol > 10:
            vol = 10        
        self.set_value(160, int(vol), nowait)

    def set_motion_area(self, x=0,y=0,xlen=50, ylen=100, nowait=False):
        """ set the area of motion detection [%] """
        if x <   0: x = 0
        if y <   0: y = 0
        if x > 100: x = 100
        if y > 100: y = 100
        if xlen <   0: xlen =   0
        if ylen <   0: ylen =   0
        if xlen > 100: xlen = 100
        if ylen > 100: ylen = 100
        if x+xlen >100: 
           x    = 25
           xlen = 75
        if y+ylen >100: 
           y    = 25
           ylen = 75
        data = '{"num":1,"region0":{"x":'+str(x)+',"y":'+str(y)+',"xlen":'+str(xlen)+',"ylen":'+str(ylen)+'}}'
        self.set_value(169, data, nowait)

    def set_motion_area_switch(self, useArea=False, nowait=False):
        """ use the area of motion detection on/off """
        self.set_value(168, bool(useArea), nowait)

if you want to test it put the DoorbellDevice.py into your tinytuya install folder and add from .DoorbellDevice import DoorbellDevice to your __init__.py (on my pi the folder for this changes is in /home/pi/.local/lib/python3.9/site-packages/tinytuya/) and replace the dummy values in d = tinytuya.DoorbellDevice() by your device data in the testfile below

import tinytuya
#tinytuya.set_debug()
"""
Devive LSC-Bell 8S(AKV300_8M)
"""
d = tinytuya.DoorbellDevice('xxxxxxxxxxxxxxxxxxx', '192.168.178.25', 'xxxxxxxxxxxxxxxxx', 'device22')
d.set_version(3.3)
d.set_socketPersistent(True) # Keep socket connection open between commands

d.set_volume(3)
d.set_motion_area(0, 5, 50, 50)
d.set_motion_area_switch(True)

print(" > Begin Monitor Loop <")
while(True):
    # See if any data is available
    data = d.receive()
    print('Data: %r' % data)
    # Send keyalive heartbeat
    print(" > Send Heartbeat Ping < ")
    payload = d.generate_payload(tinytuya.HEART_BEAT)
    d.send(payload)
jasonacox commented 2 years ago

I know the use of this is difficult due to the offline nature, but I have added your good work to our Contrib device list in case someone else discovers a similar device that does work (online) and wants to extend this. Thanks for your contribution, @jonesMeUp !