Fix flake8 issues and enable flake8 in tox
This commit is contained in:
@@ -25,6 +25,7 @@ from .const import CONF_LOCAL_KEY, CONF_PROTOCOL_VERSION, DOMAIN
|
||||
|
||||
|
||||
import pprint
|
||||
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -138,7 +139,6 @@ def get_entity_config(config_entry, dps_id):
|
||||
raise Exception(f"missing entity config for id {dps_id}")
|
||||
|
||||
|
||||
|
||||
class TuyaDevice:
|
||||
"""Cache wrapper for pytuya.TuyaInterface."""
|
||||
|
||||
@@ -160,7 +160,6 @@ class TuyaDevice:
|
||||
for i in range(5):
|
||||
try:
|
||||
status = self._interface.status()
|
||||
# print("STATUS OF [{}] IS [{}]".format(self._interface.address,status))
|
||||
return status
|
||||
except Exception:
|
||||
print(
|
||||
@@ -183,11 +182,12 @@ class TuyaDevice:
|
||||
self._cached_status_time = 0
|
||||
for i in range(5):
|
||||
try:
|
||||
#_LOGGER.info("Running a try from def set_dps from cover where state=%s and dps_index=%s", state, dps_index)
|
||||
return self._interface.set_dps(state, dps_index)
|
||||
except Exception:
|
||||
print(
|
||||
"Failed to set status of device [{}]".format(self._interface.address)
|
||||
"Failed to set status of device [{}]".format(
|
||||
self._interface.address
|
||||
)
|
||||
)
|
||||
if i + 1 == 3:
|
||||
_LOGGER.error(
|
||||
|
@@ -1,8 +1,8 @@
|
||||
"""Constants for localtuya integration."""
|
||||
|
||||
ATTR_CURRENT = 'current'
|
||||
ATTR_CURRENT_CONSUMPTION = 'current_consumption'
|
||||
ATTR_VOLTAGE = 'voltage'
|
||||
ATTR_CURRENT = "current"
|
||||
ATTR_CURRENT_CONSUMPTION = "current_consumption"
|
||||
ATTR_VOLTAGE = "voltage"
|
||||
|
||||
CONF_LOCAL_KEY = "local_key"
|
||||
CONF_PROTOCOL_VERSION = "protocol_version"
|
||||
@@ -15,9 +15,9 @@ CONF_CURRENT_CONSUMPTION = "current_consumption"
|
||||
CONF_VOLTAGE = "voltage"
|
||||
|
||||
# cover
|
||||
CONF_OPEN_CMD = 'open_cmd'
|
||||
CONF_CLOSE_CMD = 'close_cmd'
|
||||
CONF_STOP_CMD = 'stop_cmd'
|
||||
CONF_OPEN_CMD = "open_cmd"
|
||||
CONF_CLOSE_CMD = "close_cmd"
|
||||
CONF_STOP_CMD = "stop_cmd"
|
||||
|
||||
# sensor
|
||||
CONF_SCALING = "scaling"
|
||||
|
@@ -19,7 +19,7 @@ cover:
|
||||
|
||||
"""
|
||||
import logging
|
||||
from time import time, sleep
|
||||
from time import sleep
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -50,7 +50,6 @@ from .const import (
|
||||
CONF_CLOSE_CMD,
|
||||
CONF_STOP_CMD,
|
||||
)
|
||||
from .const import CONF_OPEN_CMD, CONF_CLOSE_CMD, CONF_STOP_CMD
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -79,9 +78,7 @@ def flow_schema(dps):
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up a Tuya cover based on a config entry."""
|
||||
tuyainterface, entities_to_setup = prepare_setup_entities(
|
||||
config_entry, DOMAIN
|
||||
)
|
||||
tuyainterface, entities_to_setup = prepare_setup_entities(config_entry, DOMAIN)
|
||||
if not entities_to_setup:
|
||||
return
|
||||
|
||||
@@ -97,6 +94,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
|
||||
async_add_entities(covers, True)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up of the Tuya cover."""
|
||||
return import_from_yaml(hass, config, DOMAIN)
|
||||
@@ -113,12 +111,11 @@ class LocaltuyaCover(LocalTuyaEntity, CoverEntity):
|
||||
**kwargs,
|
||||
):
|
||||
"""Initialize a new LocaltuyaCover."""
|
||||
#_LOGGER.info("running def __init__ of LocaltuyaCover(CoverEntity) with self=%s device=%s name=%s friendly_name=%s icon=%s switchid=%s open_cmd=%s close_cmd=%s stop_cmd=%s", self, device, name, friendly_name, icon, switchid, open_cmd, close_cmd, stop_cmd)
|
||||
super().__init__(device, config_entry, switchid, **kwargs)
|
||||
self._state = None
|
||||
self._position = 50
|
||||
print(
|
||||
"Initialized tuya cover [{}] with switch status [{}] and state [{}]".format(
|
||||
"Initialized cover [{}] with status [{}] and state [{}]".format(
|
||||
self.name, self._status, self._state
|
||||
)
|
||||
)
|
||||
@@ -198,7 +195,6 @@ class LocaltuyaCover(LocalTuyaEntity, CoverEntity):
|
||||
self.stop_cover()
|
||||
self._position = 50 # newpos
|
||||
|
||||
|
||||
def open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
_LOGGER.debug("Launching command %s to cover ", self._config[CONF_OPEN_CMD])
|
||||
|
@@ -18,6 +18,7 @@ UDP_KEY = md5(b"yGAdlopoPVldABfn").digest()
|
||||
|
||||
def decrypt_udp(message):
|
||||
"""Decrypt encrypted UDP broadcasts."""
|
||||
|
||||
def _unpad(data):
|
||||
return data[: -ord(data[len(data) - 1 :])]
|
||||
|
||||
|
@@ -15,7 +15,6 @@ fan:
|
||||
|
||||
"""
|
||||
import logging
|
||||
from time import time, sleep
|
||||
|
||||
from homeassistant.components.fan import (
|
||||
FanEntity,
|
||||
@@ -59,7 +58,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
for device_config in entities_to_setup:
|
||||
fans.append(
|
||||
LocaltuyaFan(
|
||||
TuyaCache(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]),
|
||||
TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]),
|
||||
config_entry,
|
||||
device_config[CONF_ID],
|
||||
)
|
||||
@@ -73,7 +72,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
return import_from_yaml(hass, config, DOMAIN)
|
||||
|
||||
|
||||
|
||||
class LocaltuyaFan(LocalTuyaEntity, FanEntity):
|
||||
"""Representation of a Tuya fan."""
|
||||
|
||||
|
@@ -11,9 +11,7 @@ light:
|
||||
friendly_name: This Light
|
||||
protocol_version: 3.3
|
||||
"""
|
||||
import socket
|
||||
import logging
|
||||
from time import time, sleep
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_ID,
|
||||
@@ -71,7 +69,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
|
||||
lights.append(
|
||||
LocaltuyaLight(
|
||||
TuyaCache(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]),
|
||||
TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]),
|
||||
config_entry,
|
||||
device_config[CONF_ID],
|
||||
)
|
||||
@@ -85,7 +83,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
return import_from_yaml(hass, config, DOMAIN)
|
||||
|
||||
|
||||
|
||||
class LocaltuyaLight(LocalTuyaEntity, LightEntity):
|
||||
"""Representation of a Tuya light."""
|
||||
|
||||
|
@@ -21,7 +21,8 @@ Functions
|
||||
json = status() # returns json payload
|
||||
set_version(version) # 3.1 [default] or 3.3
|
||||
detect_available_dps() # returns a list of available dps provided by the device
|
||||
add_dps_to_request(dps_index) # adds dps_index to the list of dps used by the device (to be queried in the payload)
|
||||
add_dps_to_request(dps_index) # adds dps_index to the list of dps used by the
|
||||
# device (to be queried in the payload)
|
||||
set_dps(on, dps_index) # Set value of any dps index.
|
||||
set_timer(num_secs):
|
||||
|
||||
@@ -37,13 +38,11 @@ Credits
|
||||
|
||||
import base64
|
||||
from hashlib import md5
|
||||
from itertools import chain
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import colorsys
|
||||
import binascii
|
||||
|
||||
try:
|
||||
@@ -160,20 +159,26 @@ def hex2bin(x):
|
||||
return bytes.fromhex(x)
|
||||
|
||||
|
||||
# This is intended to match requests.json payload at https://github.com/codetheweb/tuyapi :
|
||||
# This is intended to match requests.json payload at
|
||||
# https://github.com/codetheweb/tuyapi :
|
||||
# type_0a devices require the 0a command as the status request
|
||||
# type_0d devices require the 0d command as the status request, and the list of dps used set to null in the request payload (see generate_payload method)
|
||||
# type_0d devices require the 0d command as the status request, and the list of
|
||||
# dps used set to null in the request payload (see generate_payload method)
|
||||
|
||||
# prefix: # Next byte is command byte ("hexByte") some zero padding, then length
|
||||
# of remaining payload, i.e. command + suffix (unclear if multiple bytes used for
|
||||
# length, zero padding implies could be more than one byte)
|
||||
payload_dict = {
|
||||
"type_0a": {
|
||||
"status": {"hexByte": "0a", "command": {"gwId": "", "devId": ""}},
|
||||
"set": {"hexByte": "07", "command": {"devId": "", "uid": "", "t": ""}},
|
||||
"prefix": "000055aa00000000000000", # Next byte is command byte ("hexByte") some zero padding, then length of remaining payload, i.e. command + suffix (unclear if multiple bytes used for length, zero padding implies could be more than one byte)
|
||||
"prefix": "000055aa00000000000000",
|
||||
"suffix": "000000000000aa55",
|
||||
},
|
||||
"type_0d": {
|
||||
"status": {"hexByte": "0d", "command": {"devId": "", "uid": "", "t": ""}},
|
||||
"set": {"hexByte": "07", "command": {"devId": "", "uid": "", "t": ""}},
|
||||
"prefix": "000055aa00000000000000", # Next byte is command byte ("hexByte") some zero padding, then length of remaining payload, i.e. command + suffix (unclear if multiple bytes used for length, zero padding implies could be more than one byte)
|
||||
"prefix": "000055aa00000000000000",
|
||||
"suffix": "000000000000aa55",
|
||||
},
|
||||
}
|
||||
@@ -235,7 +240,8 @@ class TuyaInterface:
|
||||
try:
|
||||
data = s.recv(1024)
|
||||
# print("FIRST: Received %d bytes" % len(data) )
|
||||
# sometimes the first packet does not contain data (typically 28 bytes): need to read again
|
||||
# sometimes the first packet does not contain data (typically 28 bytes):
|
||||
# need to read again
|
||||
if len(data) < 40:
|
||||
time.sleep(0.1)
|
||||
data = s.recv(1024)
|
||||
@@ -250,19 +256,21 @@ class TuyaInterface:
|
||||
|
||||
def detect_available_dps(self):
|
||||
"""Return which datapoints are supported by the device."""
|
||||
# type_0d devices need a sort of bruteforce querying in order to detect the list of available dps
|
||||
# experience shows that the dps available are usually in the ranges [1-25] and [100-110]
|
||||
# need to split the bruteforcing in different steps due to request payload limitation (max. length = 255)
|
||||
# type_0d devices need a sort of bruteforce querying in order to detect the
|
||||
# list of available dps experience shows that the dps available are usually
|
||||
# in the ranges [1-25] and [100-110] need to split the bruteforcing in
|
||||
# different steps due to request payload limitation (max. length = 255)
|
||||
detected_dps = {}
|
||||
|
||||
# dps 1 must always be sent, otherwise it might fail in case no dps is found in the requested range
|
||||
# dps 1 must always be sent, otherwise it might fail in case no dps is found
|
||||
# in the requested range
|
||||
self.dps_to_request = {"1": None}
|
||||
self.add_dps_to_request(range(2, 11))
|
||||
try:
|
||||
data = self.status()
|
||||
except Exception as e:
|
||||
print("Failed to get status: [{}]".format(e))
|
||||
raise CannotConnect
|
||||
raise
|
||||
detected_dps.update(data["dps"])
|
||||
|
||||
if self.dev_type == "type_0a":
|
||||
@@ -274,7 +282,7 @@ class TuyaInterface:
|
||||
data = self.status()
|
||||
except Exception as e:
|
||||
print("Failed to get status: [{}]".format(e))
|
||||
raise CannotConnect
|
||||
raise
|
||||
detected_dps.update(data["dps"])
|
||||
|
||||
self.dps_to_request = {"1": None}
|
||||
@@ -283,7 +291,7 @@ class TuyaInterface:
|
||||
data = self.status()
|
||||
except Exception as e:
|
||||
print("Failed to get status: [{}]".format(e))
|
||||
raise CannotConnect
|
||||
raise
|
||||
detected_dps.update(data["dps"])
|
||||
|
||||
self.dps_to_request = {"1": None}
|
||||
@@ -292,7 +300,7 @@ class TuyaInterface:
|
||||
data = self.status()
|
||||
except Exception as e:
|
||||
print("Failed to get status: [{}]".format(e))
|
||||
raise CannotConnect
|
||||
raise
|
||||
detected_dps.update(data["dps"])
|
||||
# print("DATA IS [{}] detected_dps [{}]".format(data,detected_dps))
|
||||
|
||||
@@ -400,8 +408,6 @@ class TuyaInterface:
|
||||
# calc the CRC of everything except where the CRC goes and the suffix
|
||||
hex_crc = format(binascii.crc32(buffer[:-8]) & 0xFFFFFFFF, "08X")
|
||||
buffer = buffer[:-8] + hex2bin(hex_crc) + buffer[-4:]
|
||||
# print('full buffer(%d) %r' % (len(buffer), bin2hex(buffer, pretty=True) ))
|
||||
# print('full buffer(%d) %r' % (len(buffer), " ".join("{:02x}".format(ord(c)) for c in buffer)))
|
||||
return buffer
|
||||
|
||||
def status(self):
|
||||
@@ -418,7 +424,8 @@ class TuyaInterface:
|
||||
result = result[15:]
|
||||
|
||||
log.debug("result=%r", result)
|
||||
# result = data[data.find('{'):data.rfind('}')+1] # naive marker search, hope neither { nor } occur in header/footer
|
||||
# result = data[data.find('{'):data.rfind('}')+1] # naive marker search,
|
||||
# hope neither { nor } occur in header/footer
|
||||
# print('result %r' % result)
|
||||
if result.startswith(b"{"):
|
||||
# this is the regular expected code path
|
||||
@@ -427,12 +434,13 @@ class TuyaInterface:
|
||||
result = json.loads(result)
|
||||
elif result.startswith(PROTOCOL_VERSION_BYTES_31):
|
||||
# got an encrypted payload, happens occasionally
|
||||
# expect resulting json to look similar to:: {"devId":"ID","dps":{"1":true,"2":0},"t":EPOCH_SECS,"s":3_DIGIT_NUM}
|
||||
# expect resulting json to look similar to:
|
||||
# {"devId":"ID","dps":{"1":true,"2":0},"t":EPOCH_SECS,"s":3_DIGIT_NUM}
|
||||
# NOTE dps.2 may or may not be present
|
||||
result = result[len(PROTOCOL_VERSION_BYTES_31) :] # remove version header
|
||||
result = result[
|
||||
16:
|
||||
] # remove (what I'm guessing, but not confirmed is) 16-bytes of MD5 hexdigest of payload
|
||||
# remove (what I'm guessing, but not confirmed is) 16-bytes of MD5
|
||||
# hexdigest of payload
|
||||
result = result[16:]
|
||||
cipher = AESCipher(self.local_key)
|
||||
result = cipher.decrypt(result)
|
||||
print("decrypted result=[{}]".format(result))
|
||||
@@ -487,7 +495,8 @@ class TuyaInterface:
|
||||
"""
|
||||
# FIXME / TODO support schemas? Accept timer id number as parameter?
|
||||
|
||||
# Dumb heuristic; Query status, pick last device id as that is probably the timer
|
||||
# Dumb heuristic; Query status, pick last device id as that is probably
|
||||
# the timer
|
||||
status = self.status()
|
||||
devices = status["dps"]
|
||||
devices_numbers = list(devices.keys())
|
||||
|
@@ -15,7 +15,6 @@ sensor:
|
||||
device_class: current
|
||||
"""
|
||||
import logging
|
||||
from time import time, sleep
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -65,7 +64,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
for device_config in entities_to_setup:
|
||||
sensors.append(
|
||||
LocaltuyaSensor(
|
||||
TuyaCache(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]),
|
||||
TuyaDevice(tuyainterface, config_entry.data[CONF_FRIENDLY_NAME]),
|
||||
config_entry,
|
||||
device_config[CONF_ID],
|
||||
)
|
||||
@@ -79,7 +78,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
return import_from_yaml(hass, config, DOMAIN)
|
||||
|
||||
|
||||
|
||||
class LocaltuyaSensor(LocalTuyaEntity):
|
||||
"""Representation of a Tuya sensor."""
|
||||
|
||||
|
@@ -25,7 +25,6 @@ switch:
|
||||
id: 7
|
||||
"""
|
||||
import logging
|
||||
from time import time, sleep
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -104,7 +103,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
if device_config.get(CONF_CURRENT, "-1") != "-1":
|
||||
tuyainterface.add_dps_to_request(device_config.get(CONF_CURRENT))
|
||||
if device_config.get(CONF_CURRENT_CONSUMPTION, "-1") != "-1":
|
||||
tuyainterface.add_dps_to_request(device_config.get(CONF_CURRENT_CONSUMPTION))
|
||||
tuyainterface.add_dps_to_request(
|
||||
device_config.get(CONF_CURRENT_CONSUMPTION)
|
||||
)
|
||||
if device_config.get(CONF_VOLTAGE, "-1") != "-1":
|
||||
tuyainterface.add_dps_to_request(device_config.get(CONF_VOLTAGE))
|
||||
|
||||
@@ -124,8 +125,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
return import_from_yaml(hass, config, DOMAIN)
|
||||
|
||||
|
||||
|
||||
|
||||
class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):
|
||||
"""Representation of a Tuya switch."""
|
||||
|
||||
@@ -140,7 +139,7 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):
|
||||
super().__init__(device, config_entry, switchid, **kwargs)
|
||||
self._state = None
|
||||
print(
|
||||
"Initialized tuya switch [{}] with switch status [{}] and state [{}]".format(
|
||||
"Initialized switch [{}] with status [{}] and state [{}]".format(
|
||||
self.name, self._status, self._state
|
||||
)
|
||||
)
|
||||
|
@@ -1,3 +1,3 @@
|
||||
[tool.black]
|
||||
target-version = ["py37", "py38"]
|
||||
include = "custom_components/localtuya/*.py"
|
||||
include = 'custom_components/localtuya/.*\.py'
|
||||
|
@@ -1,3 +1,4 @@
|
||||
black==20.8b1
|
||||
flake8==3.8.3
|
||||
mypy==0.782
|
||||
pydocstyle==5.1.1
|
4
setup.cfg
Normal file
4
setup.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
[flake8]
|
||||
exclude = .git,.tox
|
||||
max-line-length = 88
|
||||
ignore = E203, W503
|
Reference in New Issue
Block a user