-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dfdf46c
commit 1cb6a0f
Showing
7 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
custom_components/hass-bluecon/HassDataOAuthTokenStorage.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from bluecon import IOAuthTokenStorage, OAuthToken | ||
from homeassistant.core import HomeAssistant | ||
from .const import DOMAIN | ||
|
||
class HassDataOAuthTokenStorage(IOAuthTokenStorage): | ||
def __init__(self, hass: HomeAssistant): | ||
self.__hass = hass | ||
|
||
def retrieveOAuthToken(self) -> OAuthToken: | ||
return self.__hass.data[DOMAIN]["token"] | ||
|
||
def storeOAuthToken(self, oAuthToken: OAuthToken): | ||
self.__hass.data[DOMAIN]["token"] = oAuthToken |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from .const import DOMAIN, CONF_USERNAME, CONF_PASSWORD, SIGNAL_CALL_ENDED, SIGNAL_CALL_STARTED | ||
from .HassDataOAuthTokenStorage import HassDataOAuthTokenStorage | ||
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, Platform | ||
import homeassistant.helpers.config_validation as cv | ||
import homeassistant.helpers.discovery as discovery | ||
from homeassistant.helpers.dispatcher import dispatcher_send | ||
import voluptuous as vol | ||
from bluecon import BlueConAPI, INotification, CallNotification, CallEndNotification | ||
|
||
CONFIG_SCHEMA = vol.Schema( | ||
{ | ||
DOMAIN: vol.Schema({ | ||
vol.Required(CONF_USERNAME): cv.string, | ||
vol.Required(CONF_PASSWORD): cv.string | ||
}) | ||
}, | ||
extra = vol.ALLOW_EXTRA | ||
) | ||
|
||
|
||
async def async_setup(hass, config): | ||
def notification_callback(notification: INotification): | ||
if type(notification) is CallNotification: | ||
dispatcher_send(hass, SIGNAL_CALL_STARTED.format(notification.deviceId, notification.accessDoorKey)) | ||
elif type(notification) is CallEndNotification: | ||
dispatcher_send(hass, SIGNAL_CALL_ENDED.format(notification.deviceId)) | ||
|
||
conf = config[DOMAIN] | ||
username = conf.get(CONF_USERNAME) | ||
password = conf.get(CONF_PASSWORD) | ||
|
||
hass.data[DOMAIN] = { | ||
"bluecon": None, | ||
"token": None | ||
} | ||
|
||
bluecon = await BlueConAPI.create(username, password, notification_callback, HassDataOAuthTokenStorage(hass)) | ||
|
||
async def cleanup(event): | ||
await bluecon.stopNotificationListener() | ||
|
||
async def prepare(event): | ||
await hass.async_add_executor_job(bluecon.startNotificationListener) | ||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, cleanup) | ||
|
||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, prepare) | ||
hass.data[DOMAIN]["bluecon"] = bluecon | ||
|
||
hass.async_create_task( | ||
discovery.async_load_platform(hass, Platform.BINARY_SENSOR, DOMAIN, {}, config) | ||
) | ||
|
||
hass.async_create_task( | ||
discovery.async_load_platform(hass, Platform.LOCK, DOMAIN, {}, config) | ||
) | ||
|
||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass, BinarySensorEntity | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
from homeassistant.helpers.entity import DeviceInfo | ||
from .const import DOMAIN, SIGNAL_CALL_ENDED, SIGNAL_CALL_STARTED | ||
|
||
async def async_setup_platform(hass, config, async_add_entities, discovery_info = None): | ||
bluecon = hass.data[DOMAIN]["bluecon"] | ||
|
||
pairings = await bluecon.getPairings() | ||
|
||
sensors = [] | ||
|
||
for pairing in pairings: | ||
for accessDoorName, accessDoor in pairing.accessDoorMap.items(): | ||
sensors.append( | ||
BlueConCallStatusBinarySensor( | ||
pairing.deviceId, | ||
accessDoorName, | ||
accessDoor.block, | ||
accessDoor.subBlock, | ||
accessDoor.number | ||
) | ||
) | ||
|
||
async_add_entities(sensors) | ||
|
||
class BlueConCallStatusBinarySensor(BinarySensorEntity): | ||
_attr_should_poll = False | ||
|
||
def __init__(self, deviceId, accessDoorName, block, subBlock, number): | ||
self.lockId = f'{deviceId}_{accessDoorName}' | ||
self.deviceId = deviceId | ||
self.accessDoorName = accessDoorName | ||
self.block = block | ||
self.subBlock = subBlock | ||
self.number = number | ||
self._attr_unique_id = f'{self.lockId}_call_status'.lower() | ||
self.entity_id = f'{DOMAIN}.{self._attr_unique_id}'.lower() | ||
self._attr_is_on = False | ||
|
||
@property | ||
def unique_id(self) -> str | None: | ||
return self.entity_id | ||
|
||
@property | ||
def device_class(self) -> BinarySensorDeviceClass | None: | ||
return BinarySensorDeviceClass.CONNECTIVITY | ||
|
||
async def async_added_to_hass(self) -> None: | ||
self.async_on_remove( | ||
async_dispatcher_connect(self.hass, SIGNAL_CALL_STARTED.format(self.deviceId, self.accessDoorName), self._call_started_callback) | ||
) | ||
self.async_on_remove( | ||
async_dispatcher_connect(self.hass, SIGNAL_CALL_ENDED.format(self.deviceId), self._call_ended_callback) | ||
) | ||
|
||
@callback | ||
def _call_started_callback(self) -> None: | ||
self._attr_is_on = True | ||
self.async_schedule_update_ha_state(True) | ||
|
||
@callback | ||
def _call_ended_callback(self) -> None: | ||
self._attr_is_on = False | ||
self.async_schedule_update_ha_state(True) | ||
|
||
@property | ||
def device_info(self) -> DeviceInfo | None: | ||
return DeviceInfo( | ||
identifiers = { | ||
(DOMAIN, self.lockId) | ||
}, | ||
name = f'Fermax Blue {self.lockId}', | ||
manufacturer = 'Fermax', | ||
model = 'Blue', | ||
sw_version = '0.0.1' | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
DOMAIN = "bluecon" | ||
|
||
CONF_USERNAME = "username" | ||
CONF_PASSWORD = "password" | ||
|
||
SIGNAL_CALL_STARTED = "bluecon_call_started_{}_{}" | ||
SIGNAL_CALL_ENDED = "bluecon_call_ended_{}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import asyncio | ||
from .const import DOMAIN | ||
from homeassistant.components.lock import LockEntity | ||
from homeassistant.const import ( | ||
STATE_JAMMED, | ||
STATE_LOCKED, | ||
STATE_LOCKING, | ||
STATE_UNLOCKED, | ||
STATE_UNLOCKING, | ||
) | ||
from homeassistant.helpers.entity import DeviceInfo | ||
from bluecon import BlueConAPI | ||
|
||
LOCK_UNLOCK_DELAY = 5 | ||
|
||
async def async_setup_platform(hass, config, async_add_entities, discovery_info = None): | ||
bluecon = hass.data[DOMAIN]["bluecon"] | ||
|
||
pairings = await bluecon.getPairings() | ||
|
||
locks = [] | ||
|
||
for pairing in pairings: | ||
for accessDoorName, accessDoor in pairing.accessDoorMap.items(): | ||
locks.append( | ||
BlueConLock( | ||
bluecon, | ||
pairing.deviceId, | ||
accessDoorName, | ||
accessDoor | ||
) | ||
) | ||
|
||
async_add_entities(locks) | ||
|
||
class BlueConLock(LockEntity): | ||
_attr_should_poll = False | ||
|
||
def __init__(self, bluecon: BlueConAPI, deviceId, accessDoorName, accessDoor): | ||
self.bluecon = bluecon | ||
self.lockId = f'{deviceId}_{accessDoorName}' | ||
self.deviceId = deviceId | ||
self.accessDoorName = accessDoorName | ||
self.accessDoor = accessDoor | ||
self._attr_unique_id = f'{self.lockId}_door_lock'.lower() | ||
self.entity_id = f'{DOMAIN}.{self._attr_unique_id}'.lower() | ||
self._state = STATE_LOCKED | ||
|
||
@property | ||
def is_locking(self) -> bool: | ||
"""Return true if lock is locking.""" | ||
return self._state == STATE_LOCKING | ||
|
||
@property | ||
def is_unlocking(self) -> bool: | ||
"""Return true if lock is unlocking.""" | ||
return self._state == STATE_UNLOCKING | ||
|
||
@property | ||
def is_jammed(self) -> bool: | ||
"""Return true if lock is jammed.""" | ||
return self._state == STATE_JAMMED | ||
|
||
@property | ||
def is_locked(self) -> bool: | ||
"""Return true if lock is locked.""" | ||
return self._state == STATE_LOCKED | ||
|
||
async def async_lock(self) -> None: | ||
pass | ||
|
||
async def async_unlock(self) -> None: | ||
"""Unlock the device.""" | ||
self._state = STATE_UNLOCKING | ||
self.async_schedule_update_ha_state(True) | ||
await self.bluecon.openDoor(self.deviceId, self.accessDoor) | ||
self._state = STATE_UNLOCKED | ||
self.async_schedule_update_ha_state(True) | ||
await asyncio.sleep(LOCK_UNLOCK_DELAY) | ||
self._state = STATE_LOCKED | ||
self.async_schedule_update_ha_state(True) | ||
|
||
async def async_open(self) -> None: | ||
pass | ||
|
||
@property | ||
def device_info(self) -> DeviceInfo | None: | ||
return DeviceInfo( | ||
identifiers = { | ||
(DOMAIN, self.lockId) | ||
}, | ||
name = f'Fermax Blue {self.lockId}', | ||
manufacturer = 'Fermax', | ||
model = 'Blue', | ||
sw_version = '0.0.1' | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"domain": "bluecon", | ||
"name": "Fermax Blue", | ||
"version": "0.0.1", | ||
"documentation": "https://hass-bluecon.afonsogarcia.dev/", | ||
"requirements": ["bluecon==0.0.1a5", "aiohttp", "oscrypto", "protobuf", "http-ece"], | ||
"codeowners": ["@AfonsoFGarcia"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
bluecon |