Skip to content
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
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ To be honest, this is my first time to use [`Bleak`](https://github.com/hbldh/bl
* macOS 10.15.7, Python 3.9.5
* Raspberry Pi Zero W (Raspbian GNU/Linux 10, Raspberry Pi reference 2021-05-07), Python 3.7.3

## Features
## Supported devices

- [SESAME 3](https://jp.candyhouse.co/products/sesame3)
- [SESAME bot](https://jp.candyhouse.co/products/sesame3-bot)

Please note that `pysesameos2` can only control [SESAME 3 Smart Lock](https://jp.candyhouse.co/products/sesame3) at this moment. Although all types of devices running Sesame OS2 are technically supportable, I don't actually have or need those devices. PRs are always welcome to help out!
## Features

* Scan all SESAME locks using BLE advertisements.
* Receive state changes (locked, handle position, etc.) that are actively reported from the device.
Expand All @@ -43,5 +46,6 @@ Please take a look at the [`example`](https://github.com/mochipon/pysesameos2/tr

## Credits & Thanks

* A huge thank you to all at [CANDY HOUSE](https://jp.candyhouse.co/).
* A huge thank you to all at [CANDY HOUSE](https://jp.candyhouse.co/) and their crowdfunding contributors!
* Thanks to [@Chabiichi](https://github.com/Chabiichi)-san for [the offer](https://github.com/mochipon/pysesame3/issues/25) to get my SESAME bot!
* Many thanks to [bleak](https://github.com/hbldh/bleak) and [pyzerproc](https://github.com/emlove/pyzerproc).
15 changes: 10 additions & 5 deletions example/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from concurrent.futures import ThreadPoolExecutor

from pysesameos2.ble import CHBleManager
from pysesameos2.chsesame2 import CHSesame2
from pysesameos2.device import CHDeviceKey
from pysesameos2.device import CHDeviceKey, CHSesameLock
from pysesameos2.helper import CHSesame2MechStatus, CHSesameBotMechStatus

# In order to understand the details of pysesameos2,
# here we dare to show the detailed logs.
Expand All @@ -20,7 +20,7 @@ async def ainput(prompt: str) -> str:
return await asyncio.get_event_loop().run_in_executor(executor, input, prompt)


def on_sesame_statechanged(device: CHSesame2) -> None:
def on_sesame_statechanged(device: CHSesameLock) -> None:
mech_status = device.getMechStatus()
device_status = device.getDeviceStatus()

Expand All @@ -44,7 +44,10 @@ def on_sesame_statechanged(device: CHSesame2) -> None:
print("Battery: {:.2f}V".format(mech_status.getBatteryVoltage()))
print("isInLockRange: {}".format(mech_status.isInLockRange()))
print("isInUnlockRange: {}".format(mech_status.isInUnlockRange()))
print("Position: {}".format(mech_status.getPosition()))
if isinstance(mech_status, CHSesame2MechStatus):
print("Position: {}".format(mech_status.getPosition()))
if isinstance(mech_status, CHSesameBotMechStatus):
print("Motor Status: {}".format(mech_status.getMotorStatus()))
print("=" * 10)


Expand Down Expand Up @@ -117,14 +120,16 @@ async def connect(scan_duration: int = 15):
print("=" * 10)
print("[Prompt]")
while True:
val = await ainput("Action [lock/unlock/toggle]: ")
val = await ainput("Action [lock/unlock/toggle/click]: ")

if val == "lock":
await device.lock(history_tag="My Script")
elif val == "unlock":
await device.unlock(history_tag="日本語もOK")
elif val == "toggle":
await device.toggle(history_tag="My Script")
elif val == "click":
await device.click(history_tag="日本語もOK")
else:
continue

Expand Down
44 changes: 1 addition & 43 deletions pysesameos2/ble.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import base64
import logging
import uuid
from datetime import datetime
from typing import Dict, Optional, Tuple, Union

from bleak import BleakScanner
Expand All @@ -18,11 +17,7 @@
BlePacketType,
)
from pysesameos2.device import CHDevices
from pysesameos2.helper import (
CHProductModel,
CHSesame2MechSettings,
CHSesame2MechStatus,
)
from pysesameos2.helper import CHProductModel

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -292,38 +287,6 @@ def getPayload(self) -> bytes:
return self._payload


class CHSesame2BleLoginResponse:
def __init__(self, data: bytes) -> None:
"""A representation of a response for login operation.

Args:
data (bytes): The rawdata.
"""
if not isinstance(data, bytes):
raise TypeError("Invalid data")

self._systemTime = datetime.fromtimestamp(int.from_bytes(data[0:4], "little"))
# ??? data[4:8]
self._SSM2MechSetting = CHSesame2MechSettings(rawdata=data[8:20])
self._SSM2MechStatus = CHSesame2MechStatus(rawdata=data[20:28])

def getMechSetting(self) -> CHSesame2MechSettings:
"""Return a mechanical setting of a device.

Returns:
CHSesame2MechSettings: The mechanical settinng.
"""
return self._SSM2MechSetting

def getMechStatus(self) -> CHSesame2MechStatus:
"""Return a mechanical status of a device.

Returns:
CHSesame2MechStatus: The mechanical status.
"""
return self._SSM2MechStatus


class BLEAdvertisement:
def __init__(self, dev: BLEDevice, manufacturer_data: dict) -> None:
if not isinstance(dev, BLEDevice):
Expand Down Expand Up @@ -394,11 +357,6 @@ def device_factory(self, dev: BLEDevice) -> CHDevices:

if SERVICE_UUID in dev.metadata["uuids"]:
adv = BLEAdvertisement(dev, dev.metadata["manufacturer_data"])

# TODO: Support other devices.
if adv.getProductModel() != CHProductModel.SS2:
raise NotImplementedError("The device is not supported!")

device = adv.getProductModel().deviceFactory()()
device.setAdvertisement(adv)

Expand Down
36 changes: 35 additions & 1 deletion pysesameos2/chsesame2.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import asyncio
import logging
from datetime import datetime
from typing import TYPE_CHECKING, Optional

from bleak import BleakClient
from cryptography.hazmat.primitives import cmac
from cryptography.hazmat.primitives.ciphers import algorithms

from pysesameos2.ble import (
CHSesame2BleLoginResponse,
CHSesame2BleNotify,
CHSesame2BlePayload,
CHSesame2BlePublish,
Expand All @@ -30,6 +30,7 @@
from pysesameos2.crypto import AppKeyFactory, BleCipher
from pysesameos2.device import CHSesameLock
from pysesameos2.helper import (
CHProductModel,
CHSesame2MechSettings,
CHSesame2MechStatus,
HistoryTagHelper,
Expand All @@ -42,10 +43,43 @@
logger = logging.getLogger(__name__)


class CHSesame2BleLoginResponse:
def __init__(self, data: bytes) -> None:
"""A representation of a response for login operation.

Args:
data (bytes): The rawdata.
"""
if not isinstance(data, bytes):
raise TypeError("Invalid data")

self._systemTime = datetime.fromtimestamp(int.from_bytes(data[0:4], "little"))
# ??? data[4:8]
self._SSM2MechSetting = CHSesame2MechSettings(rawdata=data[8:20])
self._SSM2MechStatus = CHSesame2MechStatus(rawdata=data[20:28])

def getMechSetting(self) -> CHSesame2MechSettings:
"""Return a mechanical setting of a device.

Returns:
CHSesame2MechSettings: The mechanical settinng.
"""
return self._SSM2MechSetting

def getMechStatus(self) -> CHSesame2MechStatus:
"""Return a mechanical status of a device.

Returns:
CHSesame2MechStatus: The mechanical status.
"""
return self._SSM2MechStatus


class CHSesame2(CHSesameLock):
def __init__(self) -> None:
"""SESAME3 Device Specific Implementation."""
super().__init__()
self.setProductModel(CHProductModel.SS2)
self._rxBuffer = CHSesame2BleReceiver()
self._txBuffer: Optional[CHSesame2BleTransmiter] = None
self._mechStatus: Optional[CHSesame2MechStatus] = None
Expand Down
Loading