Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add init commit #1

Merged
merged 2 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,10 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
os:
- ubuntu-latest
- windows-latest
- macOS-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -85,14 +80,14 @@ jobs:

# Do a dry run of PSR
- name: Test release
uses: python-semantic-release/python-semantic-release@v8.7.2
uses: bluetooth-devices/python-semantic-release@311
if: github.ref_name != 'main'
with:
root_options: --noop

# On main branch: actual PSR + upload to PyPI & GitHub
- name: Release
uses: python-semantic-release/python-semantic-release@v8.7.2
uses: bluetooth-devices/python-semantic-release@311
id: release
if: github.ref_name == 'main'
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/poetry-upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Upgrader
on:
workflow_dispatch:
schedule:
- cron: "51 21 26 * *"
- cron: "19 3 1 * *"

jobs:
upgrade:
Expand Down
286 changes: 206 additions & 80 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ packages = [
"Changelog" = "https://github.com/bluetooth-devices/leaone-ble/blob/main/CHANGELOG.md"

[tool.poetry.dependencies]
python = "^3.8"
python = "^3.11"
bluetooth-sensor-state-data = ">=1.6.2"
home-assistant-bluetooth = ">=1.10.4"
sensor-state-data = ">=2.18.0"
bluetooth-data-tools = ">=1.19.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
Expand Down
36 changes: 35 additions & 1 deletion src/leaone_ble/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,35 @@
__version__ = "0.0.0"
"""
Parser for Leaone BLE advertisements.

This file is shamelessly copied from the following repository:
https://github.com/Ernst79/bleparser/blob/c42ae922e1abed2720c7fac993777e1bd59c0c93/package/bleparser/leaone.py

MIT License applies.
"""
from __future__ import annotations

from sensor_state_data import (
DeviceClass,
DeviceKey,
SensorDescription,
SensorDeviceInfo,
SensorUpdate,
SensorValue,
Units,
)

from .parser import LeaoneBluetoothDeviceData

__version__ = "0.1.1"

__all__ = [
"LeaoneBluetoothDeviceData",
"SensorDescription",
"SensorDeviceInfo",
"DeviceClass",
"DeviceKey",
"SensorUpdate",
"SensorDeviceInfo",
"SensorValue",
"Units",
]
3 changes: 0 additions & 3 deletions src/leaone_ble/main.py

This file was deleted.

124 changes: 124 additions & 0 deletions src/leaone_ble/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""
Parser for Leaone BLE advertisements.

This file is shamelessly copied from the following repository:
https://github.com/Ernst79/bleparser/blob/c42ae922e1abed2720c7fac993777e1bd59c0c93/package/bleparser/xiaogui.py

MIT License applies.
"""
from __future__ import annotations

import logging
import struct

from bluetooth_data_tools import short_address
from bluetooth_sensor_state_data import BluetoothData
from home_assistant_bluetooth import BluetoothServiceInfo
from sensor_state_data import SensorLibrary

_LOGGER = logging.getLogger(__name__)


def address_to_bytes(address: str) -> bytes:
"""Return the address as bytes."""
return bytes([int(x, 16) for x in address.split(":")])


UNPACK_DATA = struct.Struct(">BHHHB").unpack

_DEVICE_TYPE_FROM_MODEL = {
0x20: "TZC4",
0x21: "TZC4",
0x24: "QJ-J",
0x25: "QJ-J",
0x30: "TZC4",
0x31: "TZC4",
}

LBS_TO_KGS = 0.45359237


class LeaoneBluetoothDeviceData(BluetoothData):
"""Data for Leaone BLE sensors."""

def _start_update(self, service_info: BluetoothServiceInfo) -> None:
"""Update from BLE advertisement data."""
_LOGGER.debug("Parsing Leaone BLE advertisement data: %s", service_info)
if (
not service_info.manufacturer_data
or service_info.service_data
or service_info.service_uuids
):
return

Check warning on line 52 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L52

Added line #L52 was not covered by tests
address = service_info.address
last_id = list(service_info.manufacturer_data)[-1]
last_data = service_info.manufacturer_data[last_id]
if len(last_data) != 13:
return

Check warning on line 57 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L57

Added line #L57 was not covered by tests

mac_trailer = last_data[-6:]
expected_mac = address_to_bytes(service_info.address)
if expected_mac != mac_trailer:
return

Check warning on line 62 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L62

Added line #L62 was not covered by tests

model = last_data[6]
if device_type := _DEVICE_TYPE_FROM_MODEL.get(model):
self.set_device_manufacturer("Leaone")
self.set_device_type(device_type)
name = f"{device_type} {short_address(address)}"
self.set_title(name)
self.set_device_name(name)
else:
return

Check warning on line 72 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L72

Added line #L72 was not covered by tests

changed_manufacturer_data = self.changed_manufacturer_data(service_info)
if not changed_manufacturer_data or len(changed_manufacturer_data) > 1:
# If len(changed_manufacturer_data) > 1 it means we switched
# ble adapters so we do not know which data is the latest
# and we need to wait for the next update.
return

Check warning on line 79 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L79

Added line #L79 was not covered by tests

last_id = list(changed_manufacturer_data)[-1]
data = (
int(last_id).to_bytes(2, byteorder="little")
+ changed_manufacturer_data[last_id]
)
xvalue = data[1:9]
(frame_cnt, weight, impedance, control, stabilized_byte) = UNPACK_DATA(xvalue)
self.set_precision(2)
packet_id = frame_cnt << 8 | stabilized_byte
self.update_predefined_sensor(SensorLibrary.PACKET_ID__NONE, packet_id)
if stabilized_byte in (0x20,): # KGS
self.update_predefined_sensor(
SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS,
weight / 10,
"non_stabilized_mass",
)
elif stabilized_byte in (0x21,): # KGS
self.update_predefined_sensor(

Check warning on line 98 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L98

Added line #L98 was not covered by tests
SensorLibrary.MASS__MASS_KILOGRAMS, weight / 10
)
self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance / 10)

Check warning on line 101 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L101

Added line #L101 was not covered by tests
elif stabilized_byte in (0x30,): # LBS
self.update_predefined_sensor(
SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS,
(weight / 10) * LBS_TO_KGS,
"non_stabilized_mass",
)
elif stabilized_byte in (0x31,): # LBS
self.update_predefined_sensor(
SensorLibrary.MASS__MASS_KILOGRAMS,
(weight / 10) * LBS_TO_KGS,
)
self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance / 10)
elif stabilized_byte in (0x24,): # KGS
self.update_predefined_sensor(

Check warning on line 115 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L115

Added line #L115 was not covered by tests
SensorLibrary.MASS_NON_STABILIZED__MASS_KILOGRAMS,
weight / 100,
"non_stabilized_mass",
)
elif stabilized_byte in (0x25,): # KGS
self.update_predefined_sensor(

Check warning on line 121 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L121

Added line #L121 was not covered by tests
SensorLibrary.MASS__MASS_KILOGRAMS, weight / 100
)
self.update_predefined_sensor(SensorLibrary.IMPEDANCE__OHM, impedance / 10)

Check warning on line 124 in src/leaone_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/leaone_ble/parser.py#L124

Added line #L124 was not covered by tests
6 changes: 0 additions & 6 deletions tests/test_main.py

This file was deleted.

Loading