2019-04-03 15:40:03 +00:00
""" Support for Brunt Blind Engine covers. """
2021-07-20 12:18:09 +00:00
from __future__ import annotations
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
from collections . abc import MutableMapping
2018-07-25 10:17:12 +00:00
import logging
2021-11-18 22:00:42 +00:00
from typing import Any
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
from aiohttp . client_exceptions import ClientResponseError
from brunt import BruntClientAsync , Thing
2018-07-25 10:17:12 +00:00
from homeassistant . components . cover import (
2019-07-31 19:25:30 +00:00
ATTR_POSITION ,
SUPPORT_CLOSE ,
SUPPORT_OPEN ,
SUPPORT_SET_POSITION ,
2021-12-09 08:18:20 +00:00
CoverDeviceClass ,
2020-04-25 16:07:15 +00:00
CoverEntity ,
2018-07-25 10:17:12 +00:00
)
2021-11-18 22:00:42 +00:00
from homeassistant . config_entries import SOURCE_IMPORT , ConfigEntry
from homeassistant . core import HomeAssistant , callback
from homeassistant . exceptions import HomeAssistantError
from homeassistant . helpers . entity import DeviceInfo
from homeassistant . helpers . entity_platform import AddEntitiesCallback
from homeassistant . helpers . typing import ConfigType , DiscoveryInfoType
from homeassistant . helpers . update_coordinator import (
CoordinatorEntity ,
DataUpdateCoordinator ,
)
from . const import (
ATTR_REQUEST_POSITION ,
ATTRIBUTION ,
CLOSED_POSITION ,
DATA_BAPI ,
DATA_COOR ,
DOMAIN ,
FAST_INTERVAL ,
OPEN_POSITION ,
REGULAR_INTERVAL ,
)
2018-07-25 10:17:12 +00:00
_LOGGER = logging . getLogger ( __name__ )
COVER_FEATURES = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
2021-11-18 22:00:42 +00:00
async def async_setup_platform (
hass : HomeAssistant ,
config : ConfigType ,
add_entities : AddEntitiesCallback ,
discovery_info : DiscoveryInfoType | None = None ,
) - > None :
""" Component setup, run import config flow for each entry in config. """
_LOGGER . warning (
" Loading brunt via platform config is deprecated; The configuration has been migrated to a config entry and can be safely removed from configuration.yaml "
)
hass . async_create_task (
hass . config_entries . flow . async_init (
DOMAIN , context = { " source " : SOURCE_IMPORT } , data = config
)
)
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
async def async_setup_entry (
hass : HomeAssistant ,
entry : ConfigEntry ,
async_add_entities : AddEntitiesCallback ,
) - > None :
2018-07-25 10:17:12 +00:00
""" Set up the brunt platform. """
2021-11-18 22:00:42 +00:00
bapi : BruntClientAsync = hass . data [ DOMAIN ] [ entry . entry_id ] [ DATA_BAPI ]
coordinator : DataUpdateCoordinator = hass . data [ DOMAIN ] [ entry . entry_id ] [ DATA_COOR ]
2019-07-31 19:25:30 +00:00
2021-11-18 22:00:42 +00:00
async_add_entities (
BruntDevice ( coordinator , serial , thing , bapi , entry . entry_id )
for serial , thing in coordinator . data . items ( )
)
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
class BruntDevice ( CoordinatorEntity , CoverEntity ) :
2018-07-25 10:17:12 +00:00
"""
Representation of a Brunt cover device .
Contains the common logic for all Brunt devices .
"""
2021-11-18 22:00:42 +00:00
def __init__ (
self ,
coordinator : DataUpdateCoordinator ,
serial : str ,
thing : Thing ,
bapi : BruntClientAsync ,
entry_id : str ,
) - > None :
2018-07-25 10:17:12 +00:00
""" Init the Brunt device. """
2021-11-18 22:00:42 +00:00
super ( ) . __init__ ( coordinator )
self . _attr_unique_id = serial
2018-07-25 10:17:12 +00:00
self . _bapi = bapi
2021-11-18 22:00:42 +00:00
self . _thing = thing
self . _entry_id = entry_id
self . _remove_update_listener = None
self . _attr_name = self . _thing . NAME
2021-12-09 08:18:20 +00:00
self . _attr_device_class = CoverDeviceClass . SHADE
2021-11-18 22:00:42 +00:00
self . _attr_supported_features = COVER_FEATURES
self . _attr_attribution = ATTRIBUTION
self . _attr_device_info = DeviceInfo (
identifiers = { ( DOMAIN , self . _attr_unique_id ) } ,
name = self . _attr_name ,
via_device = ( DOMAIN , self . _entry_id ) ,
manufacturer = " Brunt " ,
sw_version = self . _thing . FW_VERSION ,
model = self . _thing . MODEL ,
)
async def async_added_to_hass ( self ) - > None :
""" When entity is added to hass. """
await super ( ) . async_added_to_hass ( )
self . async_on_remove (
self . coordinator . async_add_listener ( self . _brunt_update_listener )
)
@property
def current_cover_position ( self ) - > int | None :
"""
Return current position of cover .
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
None is unknown , 0 is closed , 100 is fully open .
"""
pos = self . coordinator . data [ self . unique_id ] . currentPosition
return int ( pos ) if pos is not None else None
2018-07-25 10:17:12 +00:00
@property
2021-07-20 12:18:09 +00:00
def request_cover_position ( self ) - > int | None :
2018-07-25 10:17:12 +00:00
"""
Return request position of cover .
The request position is the position of the last request
to Brunt , at times there is a diff of 1 to current
None is unknown , 0 is closed , 100 is fully open .
"""
2021-11-18 22:00:42 +00:00
pos = self . coordinator . data [ self . unique_id ] . requestPosition
return int ( pos ) if pos is not None else None
2018-07-25 10:17:12 +00:00
@property
2021-07-20 12:18:09 +00:00
def move_state ( self ) - > int | None :
2018-07-25 10:17:12 +00:00
"""
Return current moving state of cover .
None is unknown , 0 when stopped , 1 when opening , 2 when closing
"""
2021-11-18 22:00:42 +00:00
mov = self . coordinator . data [ self . unique_id ] . moveState
return int ( mov ) if mov is not None else None
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
@property
def is_opening ( self ) - > bool :
""" Return if the cover is opening or not. """
return self . move_state == 1
@property
def is_closing ( self ) - > bool :
""" Return if the cover is closing or not. """
return self . move_state == 2
@property
def extra_state_attributes ( self ) - > MutableMapping [ str , Any ] :
""" Return the detailed device state attributes. """
return {
2021-07-20 12:18:09 +00:00
ATTR_REQUEST_POSITION : self . request_cover_position ,
}
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
@property
def is_closed ( self ) - > bool :
""" Return true if cover is closed, else False. """
return self . current_cover_position == CLOSED_POSITION
async def async_open_cover ( self , * * kwargs : Any ) - > None :
2018-07-25 10:17:12 +00:00
""" Set the cover to the open position. """
2021-11-18 22:00:42 +00:00
await self . _async_update_cover ( OPEN_POSITION )
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
async def async_close_cover ( self , * * kwargs : Any ) - > None :
2018-07-25 10:17:12 +00:00
""" Set the cover to the closed position. """
2021-11-18 22:00:42 +00:00
await self . _async_update_cover ( CLOSED_POSITION )
2018-07-25 10:17:12 +00:00
2021-11-18 22:00:42 +00:00
async def async_set_cover_position ( self , * * kwargs : Any ) - > None :
2018-07-25 10:17:12 +00:00
""" Set the cover to a specific position. """
2021-11-18 22:00:42 +00:00
await self . _async_update_cover ( int ( kwargs [ ATTR_POSITION ] ) )
async def _async_update_cover ( self , position : int ) - > None :
""" Set the cover to the new position and wait for the update to be reflected. """
try :
await self . _bapi . async_change_request_position (
position , thingUri = self . _thing . thingUri
)
except ClientResponseError as exc :
raise HomeAssistantError (
f " Unable to reposition { self . _thing . NAME } "
) from exc
self . coordinator . update_interval = FAST_INTERVAL
await self . coordinator . async_request_refresh ( )
@callback
def _brunt_update_listener ( self ) - > None :
""" Update the update interval after each refresh. """
if (
self . request_cover_position
== self . _bapi . last_requested_positions [ self . _thing . thingUri ]
and self . move_state == 0
) :
self . coordinator . update_interval = REGULAR_INTERVAL
else :
self . coordinator . update_interval = FAST_INTERVAL