Skip to content

Commit 917bbf4

Browse files
authored
enable HA ruff checks (#105)
* reduced code complexity * enabled HA ruff checks
1 parent dd88acd commit 917bbf4

26 files changed

+316
-141
lines changed

custom_components/bms_ble/coordinator.py

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from bleak.backends.device import BLEDevice
88
from bleak.exc import BleakError
9+
910
from homeassistant.components.bluetooth import async_last_service_info
1011
from homeassistant.components.bluetooth.const import DOMAIN as BLUETOOTH_DOMAIN
1112
from homeassistant.core import HomeAssistant

custom_components/bms_ble/plugins/basebms.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""Base class defintion for battery management systems (BMS)."""
22

3-
import asyncio.events
4-
import logging
53
from abc import ABCMeta, abstractmethod
4+
import asyncio.events
65
from collections.abc import Awaitable, Callable
6+
import logging
77
from statistics import fmean
88
from typing import Any, Final
99

@@ -12,10 +12,6 @@
1212
from bleak.backends.device import BLEDevice
1313
from bleak.exc import BleakError
1414
from bleak_retry_connector import establish_connection
15-
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
16-
from homeassistant.components.bluetooth.match import ble_device_matches
17-
from homeassistant.loader import BluetoothMatcherOptional
18-
from homeassistant.util.unit_conversion import _HRS_TO_SECS
1915

2016
from custom_components.bms_ble.const import (
2117
ATTR_BATTERY_CHARGING,
@@ -30,6 +26,10 @@
3026
KEY_CELL_VOLTAGE,
3127
KEY_TEMP_VALUE,
3228
)
29+
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
30+
from homeassistant.components.bluetooth.match import ble_device_matches
31+
from homeassistant.loader import BluetoothMatcherOptional
32+
from homeassistant.util.unit_conversion import _HRS_TO_SECS
3333

3434
type BMSsample = dict[str, int | float | bool]
3535

custom_components/bms_ble/plugins/cbtpwr_bms.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
"""Module to support CBT Power Smart BMS."""
22

33
import asyncio
4-
import logging
54
from collections.abc import Callable
5+
import logging
66
from typing import Any, Final
77

88
from bleak.backends.device import BLEDevice
99
from bleak.uuids import normalize_uuid_str
10-
from homeassistant.util.unit_conversion import _HRS_TO_SECS
1110

1211
from custom_components.bms_ble.const import (
1312
ATTR_BATTERY_CHARGING,
@@ -24,6 +23,7 @@
2423
KEY_CELL_VOLTAGE,
2524
KEY_DESIGN_CAP,
2625
)
26+
from homeassistant.util.unit_conversion import _HRS_TO_SECS
2727

2828
from .basebms import BaseBMS, BMSsample, crc_sum
2929

@@ -152,9 +152,18 @@ def _cell_voltages(data: bytearray) -> dict[str, float]:
152152
for idx in range(5)
153153
}
154154

155+
@staticmethod
156+
def _decode_data(cache: dict[int, bytearray]) -> BMSsample:
157+
data = {}
158+
for field, cmd, pos, size, sign, fct in BMS._FIELDS:
159+
if cmd in cache:
160+
data[field] = fct(
161+
int.from_bytes(cache[cmd][pos : pos + size], "little", signed=sign)
162+
)
163+
return data
164+
155165
async def _async_update(self) -> BMSsample:
156166
"""Update battery status information."""
157-
data = {}
158167
resp_cache = {} # variable to avoid multiple queries with same command
159168
for cmd in BMS._CMDS:
160169
LOGGER.debug("%s: request command 0x%X.", self.name, cmd)
@@ -167,23 +176,13 @@ async def _async_update(self) -> BMSsample:
167176
continue
168177
if cmd != self._data[BMS.CMD_POS]:
169178
LOGGER.debug(
170-
"%s:: incorrect response 0x%X to command 0x%X",
179+
"%s: incorrect response 0x%X to command 0x%X",
171180
self.name,
172181
self._data[BMS.CMD_POS],
173182
cmd,
174183
)
175184
resp_cache[self._data[BMS.CMD_POS]] = self._data.copy()
176185

177-
for field, cmd, pos, size, sign, fct in BMS._FIELDS:
178-
if resp_cache.get(cmd):
179-
data |= {
180-
field: fct(
181-
int.from_bytes(
182-
resp_cache[cmd][pos : pos + size], "little", signed=sign
183-
)
184-
)
185-
}
186-
187186
voltages = {}
188187
for cmd in BMS.CELL_VOLTAGE_CMDS:
189188
await self._client.write_gatt_char(
@@ -198,7 +197,8 @@ async def _async_update(self) -> BMSsample:
198197
for k in invalid:
199198
voltages.pop(k)
200199
break
201-
data |= voltages
200+
201+
data = BMS._decode_data(resp_cache)
202202

203203
# get cycle charge from design capacity and SoC
204204
if data.get(KEY_DESIGN_CAP) and data.get(ATTR_BATTERY_LEVEL):
@@ -209,4 +209,4 @@ async def _async_update(self) -> BMSsample:
209209
if data.get(ATTR_CURRENT, 0) >= 0:
210210
data.pop(ATTR_RUNTIME, None)
211211

212-
return data
212+
return data | voltages

custom_components/bms_ble/plugins/ective_bms.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""Module to support Ective BMS."""
22

33
import asyncio
4-
import logging
54
from collections.abc import Callable
5+
import logging
66
from typing import Any, Final
77

88
from bleak.backends.device import BLEDevice

custom_components/bms_ble/plugins/jikong_bms.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def _notification_handler(self, _sender, data: bytearray) -> None:
114114

115115
if (
116116
len(self._data) >= self.INFO_LEN
117-
and (data.startswith(BMS.HEAD_RSP) or data.startswith(self.HEAD_CMD))
117+
and (data.startswith((BMS.HEAD_RSP, self.HEAD_CMD)))
118118
) or not self._data.startswith(BMS.HEAD_RSP):
119119
self._data = bytearray()
120120

custom_components/bms_ble/sensor.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,8 @@
22

33
from typing import Final
44

5-
from homeassistant.components.sensor import (
6-
SensorEntity,
7-
SensorEntityDescription,
8-
)
9-
from homeassistant.components.sensor.const import (
10-
SensorDeviceClass,
11-
SensorStateClass,
12-
)
5+
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
6+
from homeassistant.components.sensor.const import SensorDeviceClass, SensorStateClass
137
from homeassistant.const import (
148
ATTR_BATTERY_LEVEL,
159
ATTR_TEMPERATURE,

pyproject.toml

+176-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
# pyproject.toml
22

3-
#[tool.setuptools.packages.find]
4-
#where = ["custom_components/"]
5-
#include = ["bms_ble"]
3+
[project]
4+
name = "BMS_BLE-HA"
5+
classifiers = [
6+
"License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
7+
"Programming Language :: Python :: 3"
8+
]
9+
description = "Integration that allows monitoring of Bluetooth Low Energy (BLE) battery management systems (BMS) from within Home Assistant."
10+
readme = "README.md"
11+
12+
[project.urls]
13+
"Source Code" = "https://github.com/patman15/BMS_BLE-HA"
14+
"Bug Reports" = "https://github.com/patman15/BMS_BLE-HA/issues"
615

716
[tool.pytest.ini_options]
817
minversion = "8.0"
@@ -14,3 +23,167 @@ testpaths = [
1423
"tests",
1524
]
1625
asyncio_mode = "auto"
26+
27+
# ruff settings from HA 2024.12.0.dev0
28+
[tool.ruff]
29+
required-version = ">=0.6.8"
30+
31+
[tool.ruff.lint]
32+
select = [
33+
"A001", # Variable {name} is shadowing a Python builtin
34+
"ASYNC210", # Async functions should not call blocking HTTP methods
35+
"ASYNC220", # Async functions should not create subprocesses with blocking methods
36+
"ASYNC221", # Async functions should not run processes with blocking methods
37+
"ASYNC222", # Async functions should not wait on processes with blocking methods
38+
"ASYNC230", # Async functions should not open files with blocking methods like open
39+
"ASYNC251", # Async functions should not call time.sleep
40+
"B002", # Python does not support the unary prefix increment
41+
"B005", # Using .strip() with multi-character strings is misleading
42+
"B007", # Loop control variable {name} not used within loop body
43+
"B014", # Exception handler with duplicate exception
44+
"B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it.
45+
"B017", # pytest.raises(BaseException) should be considered evil
46+
"B018", # Found useless attribute access. Either assign it to a variable or remove it.
47+
"B023", # Function definition does not bind loop variable {name}
48+
"B026", # Star-arg unpacking after a keyword argument is strongly discouraged
49+
"B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)?
50+
"B904", # Use raise from to specify exception cause
51+
"B905", # zip() without an explicit strict= parameter
52+
"BLE",
53+
"C", # complexity
54+
"COM818", # Trailing comma on bare tuple prohibited
55+
"D", # docstrings
56+
"DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow()
57+
"DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts)
58+
"E", # pycodestyle
59+
"F", # pyflakes/autoflake
60+
"F541", # f-string without any placeholders
61+
"FLY", # flynt
62+
"FURB", # refurb
63+
"G", # flake8-logging-format
64+
"I", # isort
65+
"INP", # flake8-no-pep420
66+
"ISC", # flake8-implicit-str-concat
67+
"ICN001", # import concentions; {name} should be imported as {asname}
68+
"LOG", # flake8-logging
69+
"N804", # First argument of a class method should be named cls
70+
"N805", # First argument of a method should be named self
71+
"N815", # Variable {name} in class scope should not be mixedCase
72+
"PERF", # Perflint
73+
"PGH", # pygrep-hooks
74+
"PIE", # flake8-pie
75+
"PL", # pylint
76+
"PT", # flake8-pytest-style
77+
"PTH", # flake8-pathlib
78+
"PYI", # flake8-pyi
79+
"RET", # flake8-return
80+
"RSE", # flake8-raise
81+
"RUF005", # Consider iterable unpacking instead of concatenation
82+
"RUF006", # Store a reference to the return value of asyncio.create_task
83+
"RUF010", # Use explicit conversion flag
84+
"RUF013", # PEP 484 prohibits implicit Optional
85+
"RUF017", # Avoid quadratic list summation
86+
"RUF018", # Avoid assignment expressions in assert statements
87+
"RUF019", # Unnecessary key check before dictionary access
88+
# "RUF100", # Unused `noqa` directive; temporarily every now and then to clean them up
89+
"S102", # Use of exec detected
90+
"S103", # bad-file-permissions
91+
"S108", # hardcoded-temp-file
92+
"S306", # suspicious-mktemp-usage
93+
"S307", # suspicious-eval-usage
94+
"S313", # suspicious-xmlc-element-tree-usage
95+
"S314", # suspicious-xml-element-tree-usage
96+
"S315", # suspicious-xml-expat-reader-usage
97+
"S316", # suspicious-xml-expat-builder-usage
98+
"S317", # suspicious-xml-sax-usage
99+
"S318", # suspicious-xml-mini-dom-usage
100+
"S319", # suspicious-xml-pull-dom-usage
101+
"S320", # suspicious-xmle-tree-usage
102+
"S601", # paramiko-call
103+
"S602", # subprocess-popen-with-shell-equals-true
104+
"S604", # call-with-shell-equals-true
105+
"S608", # hardcoded-sql-expression
106+
"S609", # unix-command-wildcard-injection
107+
"SIM", # flake8-simplify
108+
"SLF", # flake8-self
109+
"SLOT", # flake8-slots
110+
"T100", # Trace found: {name} used
111+
"T20", # flake8-print
112+
"TCH", # flake8-type-checking
113+
"TID", # Tidy imports
114+
"TRY", # tryceratops
115+
"UP", # pyupgrade
116+
"UP031", # Use format specifiers instead of percent format
117+
"UP032", # Use f-string instead of `format` call
118+
"W", # pycodestyle
119+
]
120+
121+
ignore = [
122+
"D202", # No blank lines allowed after function docstring
123+
"D203", # 1 blank line required before class docstring
124+
"D213", # Multi-line docstring summary should start at the second line
125+
"D406", # Section name should end with a newline
126+
"D407", # Section name underlining
127+
"E501", # line too long
128+
129+
# "PLC1901", # {existing} can be simplified to {replacement} as an empty string is falsey; too many false positives
130+
# "PLR0911", # Too many return statements ({returns} > {max_returns})
131+
# "PLR0912", # Too many branches ({branches} > {max_branches})
132+
"PLR0913", # Too many arguments to function call ({c_args} > {max_args})
133+
# "PLR0915", # Too many statements ({statements} > {max_statements})
134+
"PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable
135+
# "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target
136+
"PT004", # Fixture {fixture} does not return anything, add leading underscore
137+
# "PT011", # pytest.raises({exception}) is too broad, set the `match` parameter or use a more specific exception
138+
"PT018", # Assertion should be broken down into multiple parts
139+
# "RUF001", # String contains ambiguous unicode character.
140+
# "RUF002", # Docstring contains ambiguous unicode character.
141+
# "RUF003", # Comment contains ambiguous unicode character.
142+
# "RUF015", # Prefer next(...) over single element slice
143+
"SIM102", # Use a single if statement instead of nested if statements
144+
# "SIM103", # Return the condition {condition} directly
145+
# "SIM108", # Use ternary operator {contents} instead of if-else-block
146+
# "SIM115", # Use context handler for opening files
147+
148+
# Moving imports into type-checking blocks can mess with pytest.patch()
149+
"TCH001", # Move application import {} into a type-checking block
150+
"TCH002", # Move third-party import {} into a type-checking block
151+
"TCH003", # Move standard library import {} into a type-checking block
152+
153+
"TRY003", # Avoid specifying long messages outside the exception class
154+
"TRY400", # Use `logging.exception` instead of `logging.error`
155+
# Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923
156+
"UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)`
157+
158+
# May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
159+
"W191",
160+
"E111",
161+
"E114",
162+
"E117",
163+
"D206",
164+
"D300",
165+
"Q",
166+
"COM812",
167+
"COM819",
168+
"ISC001",
169+
170+
# Disabled because ruff does not understand type of __all__ generated by a function
171+
"PLE0605"
172+
]
173+
174+
175+
[tool.ruff.lint.isort]
176+
force-sort-within-sections = true
177+
known-first-party = [
178+
"homeassistant",
179+
]
180+
combine-as-imports = true
181+
split-on-trailing-comma = false
182+
183+
[tool.ruff.lint.per-file-ignores]
184+
185+
# Temporary for BMS_BLE-HA
186+
"tests/**" = ["SLF"]
187+
188+
[tool.ruff.lint.mccabe]
189+
max-complexity = 25

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
homeassistant==2024.6.0
22
pip>=21.3.1
3-
ruff==0.4.2
3+
ruff>=0.6.8
44

0 commit comments

Comments
 (0)