
    jw)                     n   d Z ddlmZ ddlZddlZ	 ddlmZ dZexs ej                  j                  d       Zdd	lmZ 	 eZer e        d
Zdez  xZZdZ ej*                  e      Zej0                  d   dk(  ZddZd ZddZd Zd Zd Zd Z ddZ!ddZ"ddZ#y# e$ r dZY w xY w# e$ r Y pw xY w)a  
 Python module to interface with Tuya WiFi smart devices

 Author: Jason A. Cox
 For more information see https://github.com/jasonacox/tinytuya

 Core Classes and Helper Functions

 Classes
  * AESCipher - Cryptography Helpers
  * XenonDevice(...) - Base Tuya Objects and Functions
        XenonDevice(dev_id, address=None, local_key="", dev_type="default", connection_timeout=5, 
            version="3.1", persist=False, cid/node_id=None, parent=None, connection_retry_limit=5, 
            connection_retry_delay=5, max_simultaneous_dps=0)
  * Device(XenonDevice) - Tuya Class for Devices

 Module Functions
    set_debug(toggle, color)                    # Activate verbose debugging output
    pack_message(msg, hmac_key=None)            # Packs a TuyaMessage() into a network packet, encrypting or adding a CRC if protocol requires
    unpack_message(data, hmac_key=None, header=None, no_retcode=False)
                                                # Unpacks a TuyaMessage() from a network packet, decrypting or checking the CRC if protocol requires
    parse_header(data)                          # Unpacks just the header part of a message into a TuyaHeader()
    find_device(dev_id=None, address=None)      # Scans network for Tuya devices with either ID = dev_id or IP = address
    device_info(dev_id)                         # Searches DEVICEFILE (usually devices.json) for devices with ID = dev_id and returns just that device
    assign_dp_mappings(tuyadevices, mappings)   # Adds mappings to all the devices in the tuyadevices list
    decrypt_udp(msg)                            # Decrypts a UDP network broadcast packet
    merge_dps_results(dest, src)                # Merge multiple receive() responses into a single dict
                                                #   `src` will be combined with and merged into `dest`

 Device Functions
    json = status()                    # returns json payload
    json = cached_status(historic=False, nowait=False)
                                       # When a persistent connection is open, this will return a cached version of the device status
                                       #   if historic=True, all seen DPs are returned even if their values might be out of date
                                       #   if historic=False, only DPs which are current are returned
                                       #   if nowait=False (the default), a status() call will be made if no cached status is available.
                                       #   if nowait=True, `None` will be returned immediately if no cached status is available.
    cache_clear()                      # Clears the cache, causing cached_status() to either call status() or return None
    subdev_query(nowait)               # query sub-device status (only for gateway devices)
    set_version(version)               # 3.1 [default], 3.2, 3.3 or 3.4
    set_socketPersistent(False/True)   # False [default] or True
    set_socketNODELAY(False/True)      # False or True [default]
    set_socketRetryLimit(integer)      # retry count limit [default 5]
    set_socketRetryDelay(integer)      # retry delay [default 5]
    set_socketTimeout(timeout)         # set connection timeout in seconds [default 5]
    set_dpsUsed(dps_to_request)        # add data points (DPS) to request
    add_dps_to_request(index)          # add data point (DPS) index set to None
    set_retry(retry=True)              # retry if response payload is truncated
    set_status(on, switch=1, nowait)   # Set status of switch to 'on' or 'off' (bool)
    set_value(index, value, nowait)    # Set int value of any index.
    set_multiple_values(index_value_dict, nowait)
                                       # Set multiple values with a single request
    heartbeat(nowait)                  # Send heartbeat to device
    updatedps(index=[1], nowait)       # Send updatedps command to device
    turn_on(switch=1, nowait)          # Turn on device / switch #
    turn_off(switch=1, nowait)         # Turn off
    set_timer(num_secs, nowait)        # Set timer for num_secs
    set_sendWait(num_secs)             # Time to wait after sending commands before pulling response
    detect_available_dps()             # Return list of DPS available from device
    generate_payload(command, data,...)# Generate TuyaMessage payload for command with data
    send(payload)                      # Send payload to device (do not wait for response)
    receive()                          # Receive payload from device

 Credits
  * TuyaAPI https://github.com/codetheweb/tuyapi by codetheweb and blackrozes
    For protocol reverse engineering
  * PyTuya https://github.com/clach04/python-tuya by clach04
    The origin of this python module (now abandoned)
  * LocalTuya https://github.com/rospogrigio/localtuya-homeassistant by rospogrigio
    Updated pytuya to support devices with Device IDs of 22 characters
  * Tuya Protocol 3.4 and 3.5 Support by uzlonewolf
    Enhancement to TuyaMessage logic for multi-payload messages

    )print_functionN)initTFwin   )	AESCipher)r      r   z%d.%d.%d	jasonacox   c                     |rdndt         rdj                  fd| D              }|S dj                  fd| D              }|S )N  c              3   >   K   | ]  }d t        |      fz    ywz%02X%sN)ord.0yspaces     ?/DATA/.local/lib/python3.12/site-packages/tinytuya/core/core.py	<genexpr>zbin2hex.<locals>.<genexpr>z   s     ?QSVUO3Qs   c              3   ,   K   | ]  }d |fz    ywr    r   s     r   r   zbin2hex.<locals>.<genexpr>|   s     :1QJ.s   )IS_PY2join)xprettyresultr   s      @r   bin2hexr   t   sI    ?Q?? M :::M    c                 Z    t         r| j                  d      S t        j                  |       S )Nhex)r   decodebytesfromhex)r   s    r   hex2binr%      s"    xx}}Qr   c                    |xr t         }| rJ|r&t        j                  dt        j                         n%t        j                  dt        j                         t        j                  t        j                         t        j                  dt               t        j                  dt        j                  t        j                         t        j                  sIt        j                  dt        j                  t        j                         t        j                  d       y	t        j                  dt        j                  t        j                         y	t        j                  t        j                         y	)
zEnable tinytuya verbose loggingz$[31;1m%(levelname)s:%(message)s[0m)formatlevelz%(levelname)s:%(message)szTinyTuya [%s]
zPython %s on %szUsing %s %s for cryptozMWarning: Crypto library does not support AES-GCM, v3.5 devices will not work!z(Using %s %s for crypto, GCM is supportedN)
HAVE_COLORloggingbasicConfigDEBUGlogsetLeveldebug__version__sysversionplatformr   CRYPTOLIB_HAS_GCM	CRYPTOLIBCRYPTOLIB_VERNOTSET)togglecolors     r   	set_debugr:      s     jEC7== 'B'--XW]]#		#[1		#S[[#,,?**II.	0C0CYE\E\]IIefII@)BUBUW`WnWnoW^^$r   c                     t        |      t        k7  rt        d      |r| sy| D ]5  }	 |d   }|d   }||v r	||   |d<   t        j	                  d|       d|d<   7 y#  t        j	                  d|       Y SxY w)z Adds mappings to all the devices in the tuyadevices list

    Parameters:
        tuyadevices = list of devices
        mappings = dict containing the mappings

    Response:
        Nothing, modifies tuyadevices in place
    z'mappings' must be a dictNid
product_idz9Cannot add DP mapping, no device id and/or product id: %rmappingzDevice %s has no mapping!)typedict
ValueErrorr-   r/   )tuyadevicesmappingsdevdevid	productids        r   assign_dp_mappingsrG      s     H~799k	IEL)I  %i0C	NII2E;!C	N 	IIRTWYs   
AA8c                 `    | dt        |       dz  z
  t        dt        |       dz  z
        z  z   S )N   )lenchrss    r   padrN      s1    SVb[ CSVb[(8$9999r   c                 >    | d t        | t        |       dz
  d          S )Nr   )r   rJ   rL   s    r   unpadrP      s&    $Ac!fqjlO$$%%r   c                 $    | d   |v ry| || d   <   y)NipTFr   )	newdevicedevicess     r   appenddevicerU      s"    '!(GIdOr   c           	      ~    | xr t         } | du rdx}x}x}x}x}x}x}x}}	nd}d}d}d}d}d}d	}d
}d}	|||||||||	f	S )NFr   z[0m[97m[1mz	[0m[32mz	[97m[0mz[0m[97m[2mz[0m[91m[1mz[0m[91m[2mz	[0m[36mz	[0m[31mz	[0m[33m)r)   )
r9   boldsubboldnormaldimalertalertdimcyanredyellows
             r   	termcolorr`      s     jE~QSSSwSS#SSS4S# (#"&(+ "s5$s6AAr   c                 8    ddl m} |j                  | ||       y)z9Scans your network for Tuya devices with output to stdoutr
   scanner)scantimer9   	forcescanN)r   rc   scan)maxretryr9   re   rc   s       r   rf   rf      s    LL(%9LEr   c                 <    ddl m} |j                  | |||||      S )ak  Scans your network for Tuya devices and returns dictionary of devices discovered
        devices = tinytuya.deviceScan(verbose)

    Parameters:
        verbose = True or False, print formatted output to stdout [Default: False]
        maxretry = The number of loops to wait to pick up UDP from all devices
        color = True or False, print output in color [Default: True]
        poll = True or False, poll dps status for devices if possible
        forcescan = True or False, force network scan for device IP addresses

    Response:
        devices = Dictionary of all devices found

    To unpack data, you can do something like this:

        devices = tinytuya.deviceScan()
        for ip in devices:
            id = devices[ip]['gwId']
            key = devices[ip]['productKey']
            vers = devices[ip]['version']
            dps = devices[ip]['dps']

    r
   rb   )verboserd   r9   pollre   byID)r   rc   rT   )ri   rg   r9   rj   re   rk   rc   s          r   
deviceScanrl      s%    0 ??7XUQUajqu?vvr   )F)TT)T)NTF)FNTTFF)$__doc__
__future__r   r*   r1   coloramar   HAVE_COLORAMAImportErrorr3   
startswithr)   crypto_helperr   	raw_inputinput	NameErrorversion_tupler2   r0   
__author__	getLogger__name__r-   version_infor   r   r%   r:   rG   rN   rP   rU   r`   rf   rl   r   r   r   <module>r|      s   IX &  
M @#,,"9"9%"@@
 $	E F"]2 2+
g! 
		!		!	 %,"N:&B(Fwk  M  		s#   B B, B)(B),B43B4