PySwitchbot 0.48.2__tar.gz → 0.50.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/PKG-INFO +1 -1
  2. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/PySwitchbot.egg-info/PKG-INFO +1 -1
  3. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/setup.py +1 -1
  4. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parser.py +13 -1
  5. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/meter.py +39 -0
  6. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/const.py +2 -0
  7. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/tests/test_adv_parser.py +125 -0
  8. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/LICENSE +0 -0
  9. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/MANIFEST.in +0 -0
  10. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/PySwitchbot.egg-info/SOURCES.txt +0 -0
  11. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/PySwitchbot.egg-info/dependency_links.txt +0 -0
  12. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/PySwitchbot.egg-info/requires.txt +0 -0
  13. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/PySwitchbot.egg-info/top_level.txt +0 -0
  14. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/README.md +0 -0
  15. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/setup.cfg +0 -0
  16. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/__init__.py +0 -0
  17. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/__init__.py +0 -0
  18. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/blind_tilt.py +0 -0
  19. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/bot.py +0 -0
  20. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/bulb.py +0 -0
  21. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/ceiling_light.py +0 -0
  22. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/contact.py +0 -0
  23. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/curtain.py +0 -0
  24. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/hub2.py +0 -0
  25. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/humidifier.py +0 -0
  26. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/light_strip.py +0 -0
  27. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/lock.py +0 -0
  28. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/motion.py +0 -0
  29. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/adv_parsers/plug.py +0 -0
  30. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/api_config.py +0 -0
  31. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/__init__.py +0 -0
  32. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/base_cover.py +0 -0
  33. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/base_light.py +0 -0
  34. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/blind_tilt.py +0 -0
  35. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/bot.py +0 -0
  36. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/bulb.py +0 -0
  37. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/ceiling_light.py +0 -0
  38. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/contact.py +0 -0
  39. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/curtain.py +0 -0
  40. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/device.py +0 -0
  41. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/humidifier.py +0 -0
  42. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/light_strip.py +0 -0
  43. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/lock.py +0 -0
  44. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/meter.py +0 -0
  45. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/motion.py +0 -0
  46. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/devices/plug.py +0 -0
  47. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/discovery.py +0 -0
  48. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/enum.py +0 -0
  49. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/switchbot/models.py +0 -0
  50. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/tests/test_base_cover.py +0 -0
  51. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/tests/test_blind_tilt.py +0 -0
  52. {pyswitchbot-0.48.2 → pyswitchbot-0.50.0}/tests/test_curtain.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PySwitchbot
3
- Version: 0.48.2
3
+ Version: 0.50.0
4
4
  Summary: A library to communicate with Switchbot
5
5
  Home-page: https://github.com/Danielhiversen/pySwitchbot/
6
6
  Author: Daniel Hjelseth Hoyer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PySwitchbot
3
- Version: 0.48.2
3
+ Version: 0.50.0
4
4
  Summary: A library to communicate with Switchbot
5
5
  Home-page: https://github.com/Danielhiversen/pySwitchbot/
6
6
  Author: Daniel Hjelseth Hoyer
@@ -10,7 +10,7 @@ setup(
10
10
  "cryptography>=39.0.0",
11
11
  "pyOpenSSL>=23.0.0",
12
12
  ],
13
- version="0.48.2",
13
+ version="0.50.0",
14
14
  description="A library to communicate with Switchbot",
15
15
  author="Daniel Hjelseth Hoyer",
16
16
  url="https://github.com/Danielhiversen/pySwitchbot/",
@@ -20,7 +20,7 @@ from .adv_parsers.hub2 import process_wohub2
20
20
  from .adv_parsers.humidifier import process_wohumidifier
21
21
  from .adv_parsers.light_strip import process_wostrip
22
22
  from .adv_parsers.lock import process_wolock, process_wolock_pro
23
- from .adv_parsers.meter import process_wosensorth
23
+ from .adv_parsers.meter import process_wosensorth, process_wosensorth_c
24
24
  from .adv_parsers.motion import process_wopresence
25
25
  from .adv_parsers.plug import process_woplugmini
26
26
  from .const import SwitchbotModel
@@ -101,6 +101,18 @@ SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
101
101
  "func": process_wosensorth,
102
102
  "manufacturer_id": 2409,
103
103
  },
104
+ "4": {
105
+ "modelName": SwitchbotModel.METER_PRO,
106
+ "modelFriendlyName": "Meter",
107
+ "func": process_wosensorth,
108
+ "manufacturer_id": 2409,
109
+ },
110
+ "5": {
111
+ "modelName": SwitchbotModel.METER_PRO_C,
112
+ "modelFriendlyName": "Meter",
113
+ "func": process_wosensorth_c,
114
+ "manufacturer_id": 2409,
115
+ },
104
116
  "v": {
105
117
  "modelName": SwitchbotModel.HUB2,
106
118
  "modelFriendlyName": "Hub 2",
@@ -41,3 +41,42 @@ def process_wosensorth(data: bytes | None, mfr_data: bytes | None) -> dict[str,
41
41
  }
42
42
 
43
43
  return _wosensorth_data
44
+
45
+
46
+ def process_wosensorth_c(data: bytes | None, mfr_data: bytes | None) -> dict[str, Any]:
47
+ """Process woSensorTH/Temp sensor services data with CO2."""
48
+ temp_data = None
49
+ battery = None
50
+
51
+ if mfr_data:
52
+ temp_data = mfr_data[8:11]
53
+
54
+ if data:
55
+ if not temp_data:
56
+ temp_data = data[3:6]
57
+ battery = data[2] & 0b01111111
58
+
59
+ if not temp_data:
60
+ return {}
61
+
62
+ _temp_sign = 1 if temp_data[1] & 0b10000000 else -1
63
+ _temp_c = _temp_sign * (
64
+ (temp_data[1] & 0b01111111) + ((temp_data[0] & 0b00001111) / 10)
65
+ )
66
+ _temp_f = (_temp_c * 9 / 5) + 32
67
+ _temp_f = (_temp_f * 10) / 10
68
+ humidity = temp_data[2] & 0b01111111
69
+
70
+ if _temp_c == 0 and humidity == 0 and battery == 0:
71
+ return {}
72
+
73
+ _wosensorth_data = {
74
+ # Data should be flat, but we keep the original structure for now
75
+ "temp": {"c": _temp_c, "f": _temp_f},
76
+ "temperature": _temp_c,
77
+ "fahrenheit": bool(temp_data[2] & 0b10000000),
78
+ "humidity": humidity,
79
+ "battery": battery,
80
+ }
81
+
82
+ return _wosensorth_data
@@ -42,6 +42,8 @@ class SwitchbotModel(StrEnum):
42
42
  CONTACT_SENSOR = "WoContact"
43
43
  LIGHT_STRIP = "WoStrip"
44
44
  METER = "WoSensorTH"
45
+ METER_PRO = "WoTHP"
46
+ METER_PRO_C = "WoTHPc"
45
47
  IO_METER = "WoIOSensorTH"
46
48
  MOTION_SENSOR = "WoPresence"
47
49
  COLOR_BULB = "WoBulb"
@@ -1560,3 +1560,128 @@ def test_parsing_lock_passive_old_firmware():
1560
1560
  rssi=-67,
1561
1561
  active=False,
1562
1562
  )
1563
+
1564
+
1565
+ def test_meter_pro_active() -> None:
1566
+ ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
1567
+ adv_data = generate_advertisement_data(
1568
+ manufacturer_data={2409: b"\xb0\xe9\xfeR\xdd\x84\x06d\x08\x97,\x00\x05"},
1569
+ service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"4\x00d"},
1570
+ rssi=-67,
1571
+ )
1572
+ result = parse_advertisement_data(ble_device, adv_data)
1573
+ assert result == SwitchBotAdvertisement(
1574
+ address="aa:bb:cc:dd:ee:ff",
1575
+ data={
1576
+ "data": {
1577
+ "battery": 100,
1578
+ "fahrenheit": False,
1579
+ "humidity": 44,
1580
+ "temp": {"c": 23.8, "f": 74.84},
1581
+ "temperature": 23.8,
1582
+ },
1583
+ "isEncrypted": False,
1584
+ "model": "4",
1585
+ "modelFriendlyName": "Meter",
1586
+ "modelName": SwitchbotModel.METER_PRO,
1587
+ "rawAdvData": b"4\x00d",
1588
+ },
1589
+ device=ble_device,
1590
+ rssi=-67,
1591
+ active=True,
1592
+ )
1593
+
1594
+
1595
+ def test_meter_pro_passive() -> None:
1596
+ ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
1597
+ adv_data = generate_advertisement_data(
1598
+ manufacturer_data={2409: b"\xb0\xe9\xfeR\xdd\x84\x06d\x08\x97,\x00\x05"},
1599
+ rssi=-67,
1600
+ )
1601
+ result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.METER_PRO)
1602
+ import pprint
1603
+
1604
+ pprint.pprint(result)
1605
+ assert result == SwitchBotAdvertisement(
1606
+ address="aa:bb:cc:dd:ee:ff",
1607
+ data={
1608
+ "data": {
1609
+ "battery": None,
1610
+ "fahrenheit": False,
1611
+ "humidity": 44,
1612
+ "temp": {"c": 23.8, "f": 74.84},
1613
+ "temperature": 23.8,
1614
+ },
1615
+ "isEncrypted": False,
1616
+ "model": "4",
1617
+ "modelFriendlyName": "Meter",
1618
+ "modelName": SwitchbotModel.METER_PRO,
1619
+ "rawAdvData": None,
1620
+ },
1621
+ device=ble_device,
1622
+ rssi=-67,
1623
+ active=False,
1624
+ )
1625
+
1626
+
1627
+ def test_meter_pro_c_active() -> None:
1628
+ ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
1629
+ adv_data = generate_advertisement_data(
1630
+ manufacturer_data={
1631
+ 2409: b"\xb0\xe9\xfeT2\x15\xb7\xe4\x07\x9b\xa4\x007\x02\xd5\x00"
1632
+ },
1633
+ service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"5\x00d"},
1634
+ rssi=-67,
1635
+ )
1636
+ result = parse_advertisement_data(ble_device, adv_data)
1637
+ assert result == SwitchBotAdvertisement(
1638
+ address="aa:bb:cc:dd:ee:ff",
1639
+ data={
1640
+ "data": {
1641
+ "battery": 100,
1642
+ "fahrenheit": True,
1643
+ "humidity": 36,
1644
+ "temp": {"c": 27.7, "f": 81.86},
1645
+ "temperature": 27.7,
1646
+ },
1647
+ "isEncrypted": False,
1648
+ "model": "5",
1649
+ "modelFriendlyName": "Meter",
1650
+ "modelName": SwitchbotModel.METER_PRO_C,
1651
+ "rawAdvData": b"5\x00d",
1652
+ },
1653
+ device=ble_device,
1654
+ rssi=-67,
1655
+ active=True,
1656
+ )
1657
+
1658
+
1659
+ def test_meter_pro_c_passive() -> None:
1660
+ ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
1661
+ adv_data = generate_advertisement_data(
1662
+ manufacturer_data={
1663
+ 2409: b"\xb0\xe9\xfeT2\x15\xb7\xe4\x07\x9b\xa4\x007\x02\xd5\x00"
1664
+ },
1665
+ rssi=-67,
1666
+ )
1667
+ result = parse_advertisement_data(ble_device, adv_data, SwitchbotModel.METER_PRO_C)
1668
+ assert result == SwitchBotAdvertisement(
1669
+ address="aa:bb:cc:dd:ee:ff",
1670
+ data={
1671
+ "data": {
1672
+ "battery": None,
1673
+ "fahrenheit": True,
1674
+ "humidity": 36,
1675
+ "temp": {"c": 27.7, "f": 81.86},
1676
+ "temperature": 27.7,
1677
+ },
1678
+ "isEncrypted": False,
1679
+ "model": "5",
1680
+ "modelFriendlyName": "Meter",
1681
+ "modelName": SwitchbotModel.METER_PRO_C,
1682
+ "rawAdvData": None,
1683
+ },
1684
+ device=ble_device,
1685
+ rssi=-67,
1686
+ active=False,
1687
+ )
File without changes
File without changes
File without changes
File without changes