Skip to content

Commit

Permalink
Merge pull request #7 from kingmercian/master
Browse files Browse the repository at this point in the history
Improve bluetooth timeout and connection abort issues
  • Loading branch information
aso824 authored Jun 1, 2023
2 parents 6514b60 + e195764 commit bcc5e55
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 15 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ Default port is 8080, add `--port [PORT]` to change.

Fetch info about lock (currently only battery state):

curl -XPOST http://localhost:8080/info -d '{"sn": "..."}'
curl -XPOST http://localhost:8080/info -d '{"sn": "...", "timeout": "5"}'

Lock or unlock:

curl -XPOST http://localhost:8080/do -d '{"sn": "...", "sign_key": "...", "action": "unlock"}'
curl -XPOST http://localhost:8080/do -d '{"sn": "...", "sign_key": "...", "timeout": "5", "action": "unlock"}'

Supported actions: `lock`, `unlock`, `temp_unlock` (unlock and lock after a while)

Expand All @@ -71,6 +71,7 @@ services:
build:
context: https://github.com/aso824/yeehack.git
dockerfile: Dockerfile
network_mode: host # this may not be necessary for all setups but works best with hosts bluetooth
ports:
- "8888:8080"
volumes:
Expand Down Expand Up @@ -113,12 +114,17 @@ rest_command:
url: http://127.0.0.1:8888/do
method: POST
content_type: 'application/json; charset=utf-8'
payload: '{"sn": "YYYYY", "sign_key": "XXXXX", "action": "lock"}'
payload: '{"sn": "YYYYY", "sign_key": "XXXXX", "timeout": "5", "action": "lock"}'
yeelock_unlock:
url: http://127.0.0.1:8888/do
method: POST
content_type: 'application/json; charset=utf-8'
payload: '{"sn": "YYYYY", "sign_key": "XXXXX", "action": "unlock"}'
payload: '{"sn": "YYYYY", "sign_key": "XXXXX", "timeout": "5", "action": "unlock"}'
yeelock_temp_unlock:
url: http://127.0.0.1:8888/do
method: POST
content_type: 'application/json; charset=utf-8'
payload: '{"sn": "YYYYY", "sign_key": "XXXXX", "timeout": "5", "action": "temp_unlock"}'
```

## Development
Expand Down
11 changes: 6 additions & 5 deletions lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@


class Lock:
def __init__(self, sn: str, sign_key: bytearray):
def __init__(self, sn: str, sign_key: bytearray, timeout: int = 5.0):
self.sn: str = sn
self.sign_key: bytearray = sign_key
self.timeout: int = timeout
self.device = None

@staticmethod
async def create(sn: str, sign_key: bytearray) -> Lock:
self = Lock(sn, sign_key)
async def create(sn: str, sign_key: bytearray, timeout: int) -> Lock:
self = Lock(sn, sign_key, timeout)

await self.__find_mac()

Expand All @@ -41,7 +42,7 @@ def callback(found: BLEDevice, advertisement_data: AdvertisementData) -> bool:

async def get_battery(self) -> int:
try:
async with BleakClient(self.device, timeout=5.0) as client:
async with BleakClient(self.device, timeout=self.timeout) as client:
connection = Connection(client, self.sign_key)
return await connection.battery_level()
except (asyncio.exceptions.TimeoutError, bleak.exc.BleakDBusError) as e:
Expand All @@ -59,7 +60,7 @@ async def temp_unlock(self) -> None:
await self.__do_action(UnlockMode.TEMP_UNLOCK)

async def __do_action(self, mode: UnlockMode) -> None:
async with BleakClient(self.device, timeout=2.0) as client:
async with BleakClient(self.device, timeout=self.timeout) as client:
connection = Connection(client, self.sign_key)

await connection.init()
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ aiohttp==3.8.3
aiosignal==1.2.0
async-timeout==4.0.2
attrs==22.1.0
bleak==0.18.1
bleak==0.20.0
certifi==2022.9.24
charset-normalizer==2.1.1
dbus-fast==1.29.1
dbus-fast==1.84.2
frozenlist==1.3.1
idna==3.4
multidict==6.0.2
Expand Down
6 changes: 4 additions & 2 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ async def info(request):
try:
data = await request.json()
sn = data['sn']
timeout = int(data['timeout'])
except (json.decoder.JSONDecodeError, AttributeError, KeyError):
return web.json_response({'error': 'Invalid input'}, status=400)

try:
lock = await Lock.create(sn, bytearray())
lock = await Lock.create(sn, bytearray(), timeout)
level = await lock.get_battery()
except errors.DeviceNotFoundError:
return web.json_response({'error': 'Device not found'}, status=400)
Expand All @@ -48,11 +49,12 @@ async def do(request):
action = data['action']
sn = data['sn']
sign_key = data['sign_key']
timeout = int(data['timeout'])
except (json.decoder.JSONDecodeError, AttributeError, KeyError):
return web.json_response({'error': 'Invalid input'}, status=400)

try:
lock = await Lock.create(sn, bytearray.fromhex(sign_key))
lock = await Lock.create(sn, bytearray.fromhex(sign_key), timeout)
except ValueError:
return web.json_response({'error': 'Sign key is not a hexadecimal string'}, status=400)
except errors.DeviceNotFoundError:
Expand Down
12 changes: 10 additions & 2 deletions yeehack.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@

parser_battery = subparsers.add_parser('battery', help='Get battery level')
parser_battery.add_argument('sn', help='Serial number (string) of your Yeelock - 8 alphanumeric characters')
parser_battery.add_argument(
'--timeout',
help='Bluetooth timeout, useful in noisy environments',
type=int, default=5.0, required=False)

parser_do = subparsers.add_parser('do', help='Execute lock/unlock/temp_unlock action')
parser_do.add_argument(
Expand All @@ -32,6 +36,10 @@
)
parser_do.add_argument('sn', help='Serial number (string) of your Yeelock - 8 alphanumeric characters')
parser_do.add_argument('sign_key', help='Bluetooth sign key from Yeelock server')
parser_do.add_argument(
'--timeout',
help='Bluetooth timeout, useful in noisy environments',
type=int, default=5.0, required=False)

parser_battery = subparsers.add_parser('fetch', help='Get info about your locks from Yeelock server')

Expand All @@ -58,13 +66,13 @@

async def main():
if args.command == 'battery':
lock = await Lock.create(args.sn, bytearray())
lock = await Lock.create(args.sn, bytearray(), args.timeout)
level = await lock.get_battery()

print("Battery level: %d%%" % level)

elif args.command == 'do':
lock = await Lock.create(args.sn, bytearray.fromhex(args.sign_key))
lock = await Lock.create(args.sn, bytearray.fromhex(args.sign_key), args.timeout)

if args.action == 'lock':
await lock.lock()
Expand Down

0 comments on commit bcc5e55

Please sign in to comment.