y2kbug

Untitled

Mar 3rd, 2022
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.51 KB | None | 0 0
  1. """Support for Sesame Japanese Version, by CANDY HOUSE."""
  2. from __future__ import annotations
  3.  
  4. import aiohttp
  5. import asyncio
  6. import base64
  7. import datetime
  8. import pysesame2
  9. import requests
  10. import voluptuous as vol
  11. from Crypto.Cipher import AES
  12. from Crypto.Hash import CMAC
  13.  
  14. import homeassistant.helpers.config_validation as cv
  15. from homeassistant.components.lock import PLATFORM_SCHEMA, LockEntity
  16. from homeassistant.const import ATTR_BATTERY_LEVEL, ATTR_DEVICE_ID, CONF_NAME, CONF_API_KEY, CONF_DEVICE_ID, \
  17.     CONF_CLIENT_SECRET
  18. from homeassistant.core import HomeAssistant
  19. from homeassistant.helpers.entity_platform import AddEntitiesCallback
  20. from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
  21.  
  22. ATTR_SERIAL_NO = "serial"
  23.  
  24. PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
  25.     vol.Required(CONF_NAME): cv.string,
  26.     vol.Required(CONF_DEVICE_ID): cv.string,
  27.     vol.Required(CONF_API_KEY): cv.string,
  28.     vol.Required(CONF_CLIENT_SECRET): cv.string,
  29. })
  30.  
  31.  
  32. def setup_platform(
  33.     hass: HomeAssistant,
  34.     config: ConfigType,
  35.     add_entities: AddEntitiesCallback,
  36.     discovery_info: DiscoveryInfoType | None = None,
  37. ) -> None:
  38.     name = config.get(CONF_NAME)
  39.     device_id = config.get(CONF_DEVICE_ID)
  40.     api_key = config.get(CONF_API_KEY)
  41.     client_secret = config.get(CONF_CLIENT_SECRET)
  42.  
  43.     add_entities(
  44.         [SesameJPDevice(
  45.             name=name,
  46.             uuid=device_id,
  47.             api_key=api_key,
  48.             secret_key=client_secret,
  49.         )],
  50.         update_before_add=True,
  51.     )
  52.  
  53.  
  54. class SesameJPDevice(LockEntity):
  55.     def __init__(
  56.             self,
  57.             name,
  58.             uuid,
  59.             api_key,
  60.             secret_key,
  61.     ) -> None:
  62.         self._name: str = name
  63.         self._uuid: str = uuid
  64.         self._api_key: str = api_key
  65.         self._secret_key: str = secret_key
  66.         self._is_locked = False
  67.         self._responsive = False
  68.         self._battery = -1
  69.         self.update()
  70.  
  71.     @property
  72.     def name(self) -> str | None:
  73.         return self._name
  74.  
  75.     @property
  76.     def available(self) -> bool:
  77.         return self._responsive
  78.  
  79.     @property
  80.     def is_locked(self) -> bool:
  81.         return self._is_locked
  82.  
  83.     def lock(self, **kwargs) -> None:
  84.         asyncio.run(self._sesame_command(action="LOCK"))
  85.  
  86.     def unlock(self, **kwargs) -> None:
  87.         asyncio.run(self._sesame_command(action="UNLOCK"))
  88.  
  89.     def open(self, **kwargs) -> None:
  90.         asyncio.run(self._sesame_command(action="UNLOCK"))
  91.  
  92.     def update(self) -> None:
  93.         response = requests.get(
  94.             "https://app.candyhouse.co/api/sesame2/{sesame2_uuid}".format(sesame2_uuid=self._uuid),
  95.             headers={
  96.                 "x-api-key": self._api_key,
  97.             },
  98.         )
  99.         state = response.json()
  100.  
  101.         if state:
  102.             self._responsive = True
  103.  
  104.             if state['CHSesame2Status'] == "locked":
  105.                 self._is_locked = True
  106.             else:
  107.                 self._is_locked = False
  108.             self._battery = state['batteryPercentage']
  109.         else:
  110.             self._responsive = False
  111.  
  112.     @property
  113.     def extra_state_attributes(self) -> dict:
  114.         return {}
  115.  
  116.     # def _sesame_history(self) -> None:
  117.     #     response = requests.get(
  118.     #         "https://app.candyhouse.co/api/sesame2/{sesame2_uuid}/history?page=0&lg=1".format(sesame2_uuid=self._uuid),
  119.     #         headers={
  120.     #             "x-api-key": self._api_key,
  121.     #         },
  122.     #     )
  123.     #     return response.json()
  124.  
  125.     async def _sesame_command(self, action) -> None:
  126.         if action == "LOCK":
  127.             cmd = 82
  128.         elif action == "UNLOCK":
  129.             cmd = 83
  130.         else:
  131.             return
  132.  
  133.         ts = int(datetime.datetime.now().timestamp())
  134.         message = ts.to_bytes(4, byteorder='little')
  135.         message = message.hex()[2:8]
  136.         cmac = CMAC.new(bytes.fromhex(self._secret_key), ciphermod=AES)
  137.         cmac.update(bytes.fromhex(message))
  138.         sign = cmac.hexdigest()
  139.  
  140.         async with aiohttp.request(
  141.             "POST",
  142.             "https://app.candyhouse.co/api/sesame2/{sesame2_uuid}/cmd".format(sesame2_uuid=self._uuid),
  143.             headers={
  144.                 "x-api-key": self._api_key,
  145.             },
  146.             json={
  147.                 "cmd": cmd,
  148.                 "history": base64.b64encode(bytes("Home-Assistant {action}".format(action=action), 'utf-8')).decode(),
  149.                 "sign": sign,
  150.             },
  151.         ) as resp:
  152.             await resp.text()
  153.  
Add Comment
Please, Sign In to add comment