Compare commits

...
11 Commits
Author SHA1 Message Date
Patrick762 f8ed8141ac Merge pull request #71 from Patrick762/bugfixes
Bugfixes and improvements
2024-03-12 21:03:07 +01:00
Patrick762 bf86fe172b update version number 2024-03-12 20:56:13 +01:00
Patrick762 babdd1755e fix dc switch, added some switches for eb3a 2024-03-12 20:55:09 +01:00
Patrick762 7596f8cee1 fix ac_output_on switches 2024-03-12 20:25:24 +01:00
Patrick762 98d2a0b73e Merge pull request #72 from Patrick762/debug-logging
Debug logging
2024-03-12 20:20:37 +01:00
Patrick762 c499fbcd9b fix writable_ranges 2024-03-12 20:13:20 +01:00
Patrick762 ec996d5728 added debug logging to init 2024-03-12 20:12:39 +01:00
Patrick762 ec657ad59b Merge pull request #66 from Patrick762/ac180-ac180p
AC180 AC180P
2024-02-25 16:48:03 +01:00
Patrick762 df8375016c prepare next beta release 2024-02-25 16:46:14 +01:00
Patrick762 8901c11afc added unittest 2024-02-25 16:41:47 +01:00
Patrick762 da5443172b added ac180 and ac180p 2024-02-25 16:41:29 +01:00
16 changed files with 180 additions and 16 deletions

No files matched your search

+2
View File
@@ -17,6 +17,8 @@ After the installation, you can use this button to install the integration:
### Supported devices: ### Supported devices:
- AC60 - AC60
- AC180 (basic data)
- AC180P (basic data)
- AC200M - AC200M
- AC300 (tested) - AC300 (tested)
- AC500 - AC500
+6
View File
@@ -33,6 +33,8 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Bluetti Powerstation from a config entry.""" """Set up Bluetti Powerstation from a config entry."""
_LOGGER.debug("Init Bluetti BT Integration")
address = entry.data.get(CONF_ADDRESS) address = entry.data.get(CONF_ADDRESS)
device_name = entry.data.get(CONF_NAME) device_name = entry.data.get(CONF_NAME)
use_controls = entry.data.get(CONF_USE_CONTROLS) use_controls = entry.data.get(CONF_USE_CONTROLS)
@@ -53,10 +55,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN][entry.entry_id].setdefault(DATA_POLLING_RUNNING, False) hass.data[DOMAIN][entry.entry_id].setdefault(DATA_POLLING_RUNNING, False)
# Create coordinator for polling # Create coordinator for polling
_LOGGER.debug("Creating coordinator")
coordinator = PollingCoordinator(hass, address, device_name, polling_interval, persistent_conn, polling_timeout, max_retries) coordinator = PollingCoordinator(hass, address, device_name, polling_interval, persistent_conn, polling_timeout, max_retries)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
hass.data[DOMAIN][entry.entry_id].setdefault(DATA_COORDINATOR, coordinator) hass.data[DOMAIN][entry.entry_id].setdefault(DATA_COORDINATOR, coordinator)
_LOGGER.debug("Creating entities")
platforms: list = PLATFORMS platforms: list = PLATFORMS
if use_controls is True: if use_controls is True:
_LOGGER.warning("You are using controls with this integration at your own risk!") _LOGGER.warning("You are using controls with this integration at your own risk!")
@@ -65,6 +69,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Setup platforms # Setup platforms
await hass.config_entries.async_forward_entry_setups(entry, platforms) await hass.config_entries.async_forward_entry_setups(entry, platforms)
_LOGGER.debug("Setup done")
return True return True
@@ -64,7 +64,7 @@ class ProtocolV1Device(BluettiDevice):
@property @property
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return [range(3006, 3008)] return [range(3006, 3009)]
@property @property
def pack_polling_commands(self) -> List[ReadHoldingRegisters]: def pack_polling_commands(self) -> List[ReadHoldingRegisters]:
@@ -0,0 +1,8 @@
"""AC180 fields."""
from ..base_devices.ProtocolV2Device import ProtocolV2Device
class AC180(ProtocolV2Device):
def __init__(self, address: str, sn: str):
super().__init__(address, "AC180", sn)
@@ -0,0 +1,8 @@
"""AC180P fields."""
from ..base_devices.ProtocolV2Device import ProtocolV2Device
class AC180P(ProtocolV2Device):
def __init__(self, address: str, sn: str):
super().__init__(address, "AC180P", sn)
@@ -58,7 +58,7 @@ class AC300(ProtocolV1Device):
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return super().writable_ranges + [ return super().writable_ranges + [
range(3001, 3016), range(3001, 3016),
3061, range(3061),
] ]
@property @property
@@ -57,7 +57,7 @@ class AC500(ProtocolV1Device):
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return super().writable_ranges + [ return super().writable_ranges + [
range(3001, 3016), range(3001, 3016),
3061, range(3061),
] ]
@property @property
@@ -28,12 +28,15 @@ class EB3A(ProtocolV1Device):
return super().polling_commands + [ return super().polling_commands + [
ReadHoldingRegisters(77, 1), ReadHoldingRegisters(77, 1),
ReadHoldingRegisters(86, 1), ReadHoldingRegisters(86, 1),
ReadHoldingRegisters(3034, 1),
ReadHoldingRegisters(3060, 1),
ReadHoldingRegisters(3063, 5),
] ]
@property @property
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return super().writable_ranges + [ return super().writable_ranges + [
3034, range(3034, 3035),
3060, range(3060, 3061),
range(3063, 3066), range(3063, 3067),
] ]
@@ -53,7 +53,7 @@ class EP500(ProtocolV1Device):
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return super().writable_ranges + [ return super().writable_ranges + [
range(3001, 3016), range(3001, 3016),
3061, range(3061),
] ]
@property @property
@@ -53,7 +53,7 @@ class EP500P(ProtocolV1Device):
def writable_ranges(self) -> List[range]: def writable_ranges(self) -> List[range]:
return super().writable_ranges + [ return super().writable_ranges + [
range(3001, 3016), range(3001, 3016),
3061, range(3061),
] ]
@property @property
@@ -18,7 +18,6 @@ class FieldType(Enum):
NUMERIC = auto() NUMERIC = auto()
BOOL = auto() BOOL = auto()
ENUM = auto() ENUM = auto()
BUTTON = auto()
class FieldAttributes: class FieldAttributes:
@@ -220,7 +219,7 @@ FIELD_ATTRIBUTES: dict[str, FieldAttributes] = {
"total_grid_feed": EnergyFieldAttributes("Total Grid Feed"), "total_grid_feed": EnergyFieldAttributes("Total Grid Feed"),
# Device specific controls # Device specific controls
"power_off": FieldAttributes( "power_off": FieldAttributes(
type=FieldType.BUTTON, type=FieldType.BOOL,
setter=True, setter=True,
name="Power Off", name="Power Off",
), ),
@@ -294,7 +293,7 @@ FIELD_ATTRIBUTES: dict[str, FieldAttributes] = {
), ),
"power_lifting_on": FieldAttributes( "power_lifting_on": FieldAttributes(
type=FieldType.BOOL, type=FieldType.BOOL,
setter=False, # Disabled for safety reasons setter=True,
name="Power Lifting", name="Power Lifting",
), ),
} }
@@ -3,6 +3,8 @@
import re import re
from ..devices.ac60 import AC60 from ..devices.ac60 import AC60
from ..devices.ac180 import AC180
from ..devices.ac180p import AC180P
from ..devices.ac200m import AC200M from ..devices.ac200m import AC200M
from ..devices.ac300 import AC300 from ..devices.ac300 import AC300
from ..devices.ac500 import AC500 from ..devices.ac500 import AC500
@@ -14,7 +16,7 @@ from ..devices.ep760 import EP760
from ..devices.ep800 import EP800 from ..devices.ep800 import EP800
DEVICE_NAME_RE = re.compile( DEVICE_NAME_RE = re.compile(
r"^(AC60|AC200M|AC300|AC500|EB3A|EP500|EP500P|EP600|EP760|EP800)(\d+)$" r"^(AC60|AC180|AC180P|AC200M|AC300|AC500|EB3A|EP500|EP500P|EP600|EP760|EP800)(\d+)$"
) )
@@ -22,6 +24,10 @@ def build_device(address: str, name: str):
match = DEVICE_NAME_RE.match(name) match = DEVICE_NAME_RE.match(name)
if match[1] == "AC60": if match[1] == "AC60":
return AC60(address, match[2]) return AC60(address, match[2])
if match[1] == "AC180":
return AC180(address, match[2])
if match[1] == "AC180P":
return AC180P(address, match[2])
if match[1] == "AC200M": if match[1] == "AC200M":
return AC200M(address, match[2]) return AC200M(address, match[2])
if match[1] == "AC300": if match[1] == "AC300":
+5 -2
View File
@@ -26,8 +26,11 @@ SUPPORTED_MODELS = [
] ]
CONTROL_FIELDS = [ CONTROL_FIELDS = [
"ac_output_on", "ac_output_on_switch",
"dc_output_on", "dc_output_on_switch",
"power_off",
"eco_on",
"power_lifting_on",
] ]
DIAGNOSTIC_FIELDS = [ DIAGNOSTIC_FIELDS = [
+3 -2
View File
@@ -2,10 +2,11 @@
"domain": "bluetti_bt", "domain": "bluetti_bt",
"name": "Bluetti BT", "name": "Bluetti BT",
"bluetooth": [ "bluetooth": [
{ "local_name": "AC60*" },
{ "local_name": "AC180*" },
{ "local_name": "AC200M*" }, { "local_name": "AC200M*" },
{ "local_name": "AC300*" }, { "local_name": "AC300*" },
{ "local_name": "AC500*" }, { "local_name": "AC500*" },
{ "local_name": "AC60*" },
{ "local_name": "EB3A*" }, { "local_name": "EB3A*" },
{ "local_name": "EP500*" }, { "local_name": "EP500*" },
{ "local_name": "EP600*" }, { "local_name": "EP600*" },
@@ -21,5 +22,5 @@
"iot_class": "local_polling", "iot_class": "local_polling",
"issue_tracker": "https://github.com/Patrick762/hassio-bluetti-bt/issues", "issue_tracker": "https://github.com/Patrick762/hassio-bluetti-bt/issues",
"requirements": [], "requirements": [],
"version": "0.0.18" "version": "0.0.20"
} }
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
# Execute unittests of python library
python3 -m unittest discover -s tests -p "*_test.py"
+124
View File
@@ -0,0 +1,124 @@
"""Unittest for device builder."""
import unittest
from custom_components.bluetti_bt.bluetti_bt_lib.utils.device_builder import build_device
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ac60 import AC60
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ac180 import AC180
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ac180p import AC180P
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ac200m import AC200M
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ac300 import AC300
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ac500 import AC500
from custom_components.bluetti_bt.bluetti_bt_lib.devices.eb3a import EB3A
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ep500 import EP500
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ep500p import EP500P
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ep600 import EP600
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ep760 import EP760
from custom_components.bluetti_bt.bluetti_bt_lib.devices.ep800 import EP800
class TestDeviceBuilder(unittest.TestCase):
def test_build_Unknow(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "PBOX56786746478"
with self.assertRaises(TypeError):
build_device(bt_addr, bt_name)
def test_build_ac60(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "AC6056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, AC60)
self.assertEqual(built.address, bt_addr)
def test_build_ac180(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "AC18056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, AC180)
self.assertEqual(built.address, bt_addr)
def test_build_ac180p(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "AC180P56786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, AC180P)
self.assertEqual(built.address, bt_addr)
def test_build_ac200m(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "AC200M56786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, AC200M)
self.assertEqual(built.address, bt_addr)
def test_build_ac300(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "AC30056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, AC300)
self.assertEqual(built.address, bt_addr)
def test_build_ac500(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "AC50056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, AC500)
self.assertEqual(built.address, bt_addr)
def test_build_eb3a(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "EB3A56786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, EB3A)
self.assertEqual(built.address, bt_addr)
def test_build_ep500(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "EP50056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, EP500)
self.assertEqual(built.address, bt_addr)
def test_build_ep500p(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "EP500P56786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, EP500P)
self.assertEqual(built.address, bt_addr)
def test_build_EP600(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "EP60056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, EP600)
self.assertEqual(built.address, bt_addr)
def test_build_EP760(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "EP76056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, EP760)
self.assertEqual(built.address, bt_addr)
def test_build_EP800(self):
bt_addr = "aa:bb:cc:dd:ee:ff"
bt_name = "EP80056786746478"
built = build_device(bt_addr, bt_name)
self.assertIsInstance(built, EP800)
self.assertEqual(built.address, bt_addr)
if __name__ == '__main__':
unittest.main()