Skip to content

Commit 0b5bec0

Browse files
committed
Init project
0 parents  commit 0b5bec0

24 files changed

+1667
-0
lines changed

.github/workflows/release.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- '*'
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
environment: release
12+
permissions:
13+
contents: write
14+
id-token: write
15+
steps:
16+
- uses: actions/checkout@v4
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.10"
21+
- name: Install dependencies
22+
run: |
23+
python -m pip install --upgrade pip wheel setuptools
24+
pip install pytest hatch versioningit
25+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
26+
- name: Build dist
27+
run: |
28+
hatch build
29+
- name: Test with pytest
30+
run: |
31+
pytest ./tests/
32+
- name: Upload release
33+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
34+
uses: softprops/action-gh-release@v1
35+
with:
36+
files: dist/*
37+
- name: Publish package
38+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
39+
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/test.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [ "master" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
name: Test
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: "3.10"
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip wheel setuptools
25+
pip install pytest pytest-cov
26+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
27+
- name: Test with pytest
28+
run: |
29+
pytest --cov=src ./tests/

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea/
2+
.coverage
3+
venv/
4+
dist/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 XFY9326
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Xiaomi NDEF
2+
3+
![!python-versions](https://img.shields.io/badge/Python-3.10-blue)
4+
[![Pypi](https://img.shields.io/pypi/v/xiaomi_ndef?color=orange)](https://pypi.org/project/xiaomi_ndef/)
5+
6+
[![Test](https://github.com/XFY9326/XiaomiNDEF/actions/workflows/test.yml/badge.svg)](https://github.com/XFY9326/XiaomiNDEF/actions/workflows/test.yml)
7+
[![Release](https://github.com/XFY9326/XiaomiNDEF/actions/workflows/release.yml/badge.svg)](https://github.com/XFY9326/XiaomiNDEF/actions/workflows/release.yml)
8+
9+
Encode and decode NDEF message using Xiaomi NFC protocol.
10+
11+
## Usage
12+
13+
```python
14+
from pyndef import NdefMessage
15+
16+
from xiaomi_ndef import MiConnectData, XiaomiNfcPayload, V2NfcProtocol
17+
from xiaomi_ndef import ndef, xiaomi, handoff, tag
18+
19+
# Example
20+
NDEF_MSG_BYTES = b"..."
21+
22+
# Parse ndef
23+
ndef_msg = NdefMessage.parse(NDEF_MSG_BYTES)
24+
25+
ndef_type = ndef.get_xiami_ndef_payload_type(ndef_msg)
26+
ndef_bytes = ndef.get_xiami_ndef_payload_bytes(ndef_msg, ndef_type)
27+
28+
# Parse ndef payload
29+
mi_connect_data = MiConnectData.parse(ndef_bytes)
30+
nfc_protocol = mi_connect_data.get_nfc_protocol()
31+
nfc_payload = mi_connect_data.to_xiaomi_nfc_payload(nfc_protocol)
32+
33+
# Build new screen mirror record
34+
handoff_ndef_type, handoff_payload = xiaomi.new_handoff_screen_mirror(
35+
device_type=handoff.DeviceType.PC,
36+
bluetooth_mac="00:00:00:00:00:00",
37+
enable_lyra=True
38+
)
39+
handoff_record = ndef.new_xiaomi_ndef_record(handoff_ndef_type, handoff_payload)
40+
41+
# Build new ndef msg
42+
handoff_msg = NdefMessage(handoff_record)
43+
print(handoff_msg.to_bytes().hex())
44+
45+
# Customize xiaomi ndef
46+
XiaomiNfcPayload(
47+
major_version=1,
48+
minor_version=11,
49+
id_hash=0,
50+
protocol=V2NfcProtocol,
51+
appData=tag.NfcTagAppData(
52+
major_version=1,
53+
minor_version=0,
54+
write_time=1666666666,
55+
flags=0,
56+
records=(
57+
tag.NfcTagDeviceRecord(
58+
device_type=tag.DeviceType.MI_SOUND_BOX,
59+
flags=0,
60+
device_number=0,
61+
attributes_map=tag.NfcTagDeviceRecord.new_attributes_map([
62+
tag.DeviceAttribute.WIFI_MAC_ADDRESS.new_pair("00:00:00:00:00:00"),
63+
tag.DeviceAttribute.BLUETOOTH_MAC_ADDRESS.new_pair("00:00:00:00:00:01")
64+
])
65+
),
66+
tag.NfcTagActionRecord(
67+
action=tag.Action.CUSTOM,
68+
condition=tag.Condition.AUTO,
69+
device_number=0,
70+
flags=0
71+
)
72+
)
73+
)
74+
)
75+
```
76+
77+
## License
78+
79+
```text
80+
MIT License
81+
82+
Copyright (c) 2024 XFY9326
83+
84+
Permission is hereby granted, free of charge, to any person obtaining a copy
85+
of this software and associated documentation files (the "Software"), to deal
86+
in the Software without restriction, including without limitation the rights
87+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
88+
copies of the Software, and to permit persons to whom the Software is
89+
furnished to do so, subject to the following conditions:
90+
91+
The above copyright notice and this permission notice shall be included in all
92+
copies or substantial portions of the Software.
93+
94+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
95+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
96+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
97+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
98+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
99+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
100+
SOFTWARE.
101+
```

build_protobuf.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import os
2+
from pathlib import Path
3+
4+
PROJECT_ROOT = Path(__file__).parent
5+
6+
PROTOC_PATH = "protoc"
7+
PROTO_DIR = PROJECT_ROOT.joinpath("proto")
8+
PYTHON_OUTPUT_DIR = PROJECT_ROOT.joinpath("src", "xiaomi_ndef", "proto")
9+
10+
if __name__ == "__main__":
11+
if os.system(f"{PROTOC_PATH} --version") != 0:
12+
print("protoc not found")
13+
exit(1)
14+
15+
old_files = [i for i in os.listdir(PYTHON_OUTPUT_DIR) if i.endswith(("_pb2.py", "_pb2.pyi"))]
16+
if len(old_files) > 0:
17+
print("Removing old protobuf files")
18+
for file in old_files:
19+
os.remove(PYTHON_OUTPUT_DIR.joinpath(file))
20+
21+
print("Creating protobuf files")
22+
os.system(" ".join([
23+
str(PROTOC_PATH),
24+
f"--proto_path=\"{PROTO_DIR}\"",
25+
f"--pyi_out=\"{PYTHON_OUTPUT_DIR}\"",
26+
f"--python_out=\"{PYTHON_OUTPUT_DIR}\"",
27+
f"\"{PROTO_DIR}/*\""
28+
]))

main.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from xiaomi_ndef import *
2+
3+
BYTES_DATA = bytes.fromhex(
4+
"0a4b0801100d2201032a094d492d4e4643544147380f4a3127" +
5+
"1700000003000e5441475f444953434f564552454465064d49" +
6+
"52524f52011130303a30303a30303a30303a30303a30306a02" +
7+
"fa7f"
8+
)
9+
NEW_DATA = tag.NfcTagAppData(
10+
major_version=1,
11+
minor_version=0,
12+
write_time=0000000000,
13+
flags=0,
14+
records=(
15+
tag.NfcTagDeviceRecord(
16+
device_type=tag.DeviceType.MI_SOUND_BOX,
17+
flags=0,
18+
device_number=0,
19+
attributes_map=tag.NfcTagDeviceRecord.new_attributes_map(
20+
[
21+
tag.DeviceAttribute.BLUETOOTH_MAC_ADDRESS.new_pair(b"\x00\x00\x00\x00\x00\x01")
22+
]
23+
)
24+
),
25+
tag.NfcTagActionRecord(
26+
action=tag.Action.AUTO,
27+
condition=tag.Condition.AUTO,
28+
device_number=0,
29+
flags=0,
30+
condition_parameters=None,
31+
)
32+
)
33+
)
34+
35+
36+
def main() -> None:
37+
mi_connect_data = MiConnectData.parse(BYTES_DATA)
38+
print(mi_connect_data)
39+
nfc_protocol = mi_connect_data.get_nfc_protocol()
40+
print(nfc_protocol)
41+
nfc_payload = mi_connect_data.to_xiaomi_nfc_payload(nfc_protocol)
42+
print(nfc_payload)
43+
print(NEW_DATA)
44+
print(NEW_DATA.encode().hex())
45+
print(
46+
MiConnectData.from_nfc_payload(
47+
XiaomiNfcPayload(
48+
major_version=1,
49+
minor_version=0,
50+
id_hash=None,
51+
protocol=V1NfcProtocol,
52+
appData=NEW_DATA
53+
)
54+
).to_bytes().hex()
55+
)
56+
57+
58+
if __name__ == "__main__":
59+
main()

proto/MiConnectProtocol.proto

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
syntax = "proto3";
2+
3+
package proto;
4+
5+
// Extracted from: com.xiaomi.mi_connect_service
6+
// Original file: AttrProto.proto
7+
8+
message Payload {
9+
int32 versionMajor = 1;
10+
int32 versionMinor = 2;
11+
bytes apps = 3;//已过时。请使用appIds替代。
12+
bytes flags = 4;
13+
string name = 5;
14+
bytes idHash = 6;
15+
int32 deviceType = 7;
16+
int32 securityMode = 8;
17+
repeated bytes appsData = 9;
18+
repeated bytes supportSetting = 10;
19+
repeated bytes currentSetting = 11;
20+
string wifiMac = 12;
21+
repeated int32 appIds = 13;//引入appIds之前使用的是apps
22+
int32 commData = 14;
23+
bool ziped = 15;
24+
string wiredMac = 16;//有线网卡mac
25+
string btMac = 17; //Bluetooth Device MAC
26+
}
27+
28+
message Container {
29+
Payload data = 1;
30+
int32 sequenceId = 14;
31+
}

pyproject.toml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
[build-system]
2+
requires = ["hatchling", "versioningit"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
dynamic = ["version"]
7+
name = "xiaomi-ndef"
8+
requires-python = ">=3.10"
9+
authors = [
10+
{ name = "XFY9326" }
11+
]
12+
maintainers = [
13+
{ name = "XFY9326" }
14+
]
15+
description = "Encode and decode NDEF message using Xiaomi NFC protocol."
16+
readme = "README.md"
17+
license = "MIT"
18+
keywords = ["nfc", "ndef", "ndef-library", "xiaomi", "mi"]
19+
classifiers = [
20+
"Development Status :: 4 - Beta",
21+
"Intended Audience :: Developers",
22+
"License :: OSI Approved :: MIT License",
23+
"Programming Language :: Python",
24+
"Programming Language :: Python :: 3",
25+
"Programming Language :: Python :: 3.10"
26+
]
27+
dependencies = [
28+
"pyndef>=1.0.1",
29+
"protobuf>=4.25.3",
30+
]
31+
32+
[tool.pytest.ini_options]
33+
pythonpath = "src"
34+
35+
[tool.hatch.build.targets.sdist]
36+
exclude = [".github/", "/proto/", "/requirements.txt", "/main.py", "/build_protobuf.py"]
37+
38+
[tool.hatch.version]
39+
source = "versioningit"

requirements.txt

64 Bytes
Binary file not shown.

src/xiaomi_ndef/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .base import UInt8BytesMap, UInt16BytesMap
2+
from .handoff import HandoffAppData
3+
from .mi_connect import MiConnectData
4+
from .nfc import XiaomiNfcPayload, XiaomiNfcProtocol, V1NfcProtocol, V2NfcProtocol, HandoffNfcProtocol
5+
from .tnf import XiaomiNdefTNF
6+
from .tag import NfcTagAppData, NfcTagRecord, NfcTagActionRecord, NfcTagDeviceRecord

0 commit comments

Comments
 (0)