Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
d936b4c
add BleBox integration (11 devices)
gadgetmobile Mar 11, 2020
764e419
bump blebox_uniapi version
gadgetmobile Mar 15, 2020
f2e11a9
disable harmless pylint warnings (TODOs)
gadgetmobile Mar 15, 2020
9ffdefa
increase coverage + cleanup TODOs
gadgetmobile Mar 17, 2020
5e8016b
limit PR to one platform (covers)
gadgetmobile Mar 17, 2020
8386c99
increase coverage + cleanup TODOs
gadgetmobile Mar 17, 2020
6d90f25
remove unused pylint comment
gadgetmobile Mar 17, 2020
4bc4354
set PARALLEL_UPDATES=1 for current blebox_uniapi
gadgetmobile Mar 17, 2020
9e88d98
remove TODO comment
gadgetmobile Mar 17, 2020
5029946
add missing errors.py file
gadgetmobile Mar 17, 2020
80753c4
rework config flow
gadgetmobile Mar 26, 2020
e09a46f
reorganize strings + translations
gadgetmobile Mar 26, 2020
8a78b42
use a fixed timeout of 3 seconds
gadgetmobile Mar 26, 2020
a9a6077
allow parallel updates
gadgetmobile Mar 26, 2020
64a1a17
bump blebox_uniapi to 1.2.0
gadgetmobile Mar 26, 2020
bec878a
rework error handling + increase coverage
gadgetmobile Mar 26, 2020
6f6ddbf
increase coverage to 100%
gadgetmobile Mar 26, 2020
7cca926
fix pylint error
gadgetmobile Mar 26, 2020
94cd788
Update homeassistant/components/blebox/__init__.py
gadgetmobile Mar 30, 2020
e3b472d
Update homeassistant/components/blebox/__init__.py
gadgetmobile Mar 30, 2020
511cda8
Update homeassistant/components/blebox/__init__.py
gadgetmobile Mar 30, 2020
5f8a90e
Update homeassistant/components/blebox/cover.py
gadgetmobile Mar 30, 2020
1ac1425
rename CommonEntity to BleBoxEntity
gadgetmobile Mar 30, 2020
d85be83
bump to blebox_uniapi 1.3.0
gadgetmobile Mar 31, 2020
68a37cf
add device registry entries
gadgetmobile Mar 31, 2020
d2d08bd
fix docsstring
gadgetmobile Mar 31, 2020
3d6ff37
remove async_setup_platform
gadgetmobile Mar 30, 2020
35fed4d
make timeout a const
gadgetmobile Mar 30, 2020
a87d90f
improve error handling
gadgetmobile Mar 31, 2020
b5ba36d
refactor config_flow
gadgetmobile Mar 30, 2020
0888a11
raise if set_cover_position is not supported
gadgetmobile Mar 31, 2020
fd19d76
remove test classes
gadgetmobile Mar 31, 2020
136480c
remove unused semver dependency
gadgetmobile Mar 31, 2020
0dba805
rename handle to handle_step_exception
gadgetmobile Mar 31, 2020
c892048
change param order for bitwise AND operator
gadgetmobile Mar 31, 2020
58276f3
move state and device class maps to consts
gadgetmobile Mar 31, 2020
6edba6d
rename method to async_create_blebox_entities
gadgetmobile Mar 31, 2020
127dc76
improve naming clarity and consistency
gadgetmobile Mar 31, 2020
88c9120
refactor async_create_blebox_entities
gadgetmobile Mar 31, 2020
bce496f
use new DEVICE_CLASS_GATE constant
gadgetmobile Mar 31, 2020
dad247a
regenerate dependency requirements
gadgetmobile Mar 31, 2020
f1a99b3
update broken test
gadgetmobile Apr 1, 2020
ac823c5
fix pylint error
gadgetmobile Apr 1, 2020
aad0ddb
rework shared tests
gadgetmobile Apr 1, 2020
e25efaf
remove unused CONFIG_SCHEMA
gadgetmobile Apr 2, 2020
74efef9
refactor + improve config_flow tests
gadgetmobile Apr 2, 2020
0b7a21f
fix typo in TODO
gadgetmobile Apr 2, 2020
5aa08b0
bump blebox_uniapi dependency to 1.3.1
gadgetmobile Apr 2, 2020
fc14b85
use ConfigEntryNotReady and rework setup
gadgetmobile Apr 15, 2020
b43e799
fix pyupgrade errors
gadgetmobile Apr 15, 2020
6e89ab6
log errors only for unknown exceptions
gadgetmobile Apr 21, 2020
f8f09da
move constants to consts.py
gadgetmobile Apr 21, 2020
13039e8
remove unused translation strings
gadgetmobile Apr 21, 2020
8410dd8
add comment for translations string consts
gadgetmobile Apr 21, 2020
83c5813
improve component unloading + missing test
gadgetmobile Apr 21, 2020
3b6883f
fix integration translation validation for v0.109
gadgetmobile Apr 21, 2020
6aa8630
update docstrings
gadgetmobile Apr 23, 2020
f1fe2a4
avoid using format()
gadgetmobile Apr 23, 2020
5ab6925
remove unused file
gadgetmobile Apr 24, 2020
e954a85
rename translations dir to match new HA version
gadgetmobile Apr 24, 2020
6df4121
properly use async_init in tests + refactor
gadgetmobile Apr 24, 2020
2d97e5e
remove async property on non-async method
gadgetmobile May 2, 2020
2dd6e59
remove return value which is never used
gadgetmobile May 2, 2020
26a393a
implicitly inherit Entity for clarity
gadgetmobile May 2, 2020
eaf9913
inline method which is used only once
gadgetmobile May 2, 2020
d15b191
bump blebox_uniapi version to 1.3.2
gadgetmobile May 2, 2020
1e67f8d
stop sharing logger with blebox_uniapi
gadgetmobile May 2, 2020
3b86818
avoid inheriting deprecated CoverDevice class
gadgetmobile May 2, 2020
e349efc
remove property side-effect
gadgetmobile May 2, 2020
d84fa14
assume core handles unsupported case properly
gadgetmobile May 2, 2020
ca612d7
remove unnecessary position presence checking
gadgetmobile May 2, 2020
d94f49e
remove empty dependencies section in manifest
gadgetmobile May 2, 2020
10c1998
rename constant to ADDRESS_ALREADY_CONFIGURED
gadgetmobile May 2, 2020
6b119fa
use tests.async_mock instead of asynctest
gadgetmobile May 2, 2020
bd70c23
rework cover tests and helper classes
gadgetmobile May 3, 2020
d4187c4
remove test helper methods
gadgetmobile May 3, 2020
9cde7b0
assure that async_update is initially properly mocked
gadgetmobile May 3, 2020
7712790
remove wrapper class
gadgetmobile May 3, 2020
c1c4900
rename entity to entry
gadgetmobile May 4, 2020
2f1c14d
properly use async_setup and caplog in tests
gadgetmobile May 4, 2020
4ecbc62
fix pylint warning about unused var
gadgetmobile May 4, 2020
92e76e6
check entry state properly
gadgetmobile May 5, 2020
e27c7f4
remove impossible test case
gadgetmobile May 5, 2020
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
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ homeassistant/components/azure_service_bus/* @hfurubotten
homeassistant/components/beewi_smartclim/* @alemuro
homeassistant/components/bitcoin/* @fabaff
homeassistant/components/bizkaibus/* @UgaitzEtxebarria
homeassistant/components/blebox/* @gadgetmobile
homeassistant/components/blink/* @fronzbot
homeassistant/components/bmp280/* @belidzs
homeassistant/components/bmw_connected_drive/* @gerard33
Expand Down
121 changes: 121 additions & 0 deletions homeassistant/components/blebox/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""The BleBox devices integration."""
import asyncio
import logging

from blebox_uniapi.error import Error
from blebox_uniapi.products import Products
from blebox_uniapi.session import ApiHost

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import Entity

from .const import DEFAULT_SETUP_TIMEOUT, DOMAIN, PRODUCT

_LOGGER = logging.getLogger(__name__)

PLATFORMS = ["cover"]

PARALLEL_UPDATES = 0


async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the BleBox devices component."""
return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up BleBox devices from a config entry."""

websession = async_get_clientsession(hass)

host = entry.data[CONF_HOST]
port = entry.data[CONF_PORT]
timeout = DEFAULT_SETUP_TIMEOUT

api_host = ApiHost(host, port, timeout, websession, hass.loop)

try:
product = await Products.async_from_host(api_host)
except Error as ex:
_LOGGER.error("Identify failed at %s:%d (%s)", api_host.host, api_host.port, ex)
raise ConfigEntryNotReady from ex

domain = hass.data.setdefault(DOMAIN, {})
domain_entry = domain.setdefault(entry.entry_id, {})
product = domain_entry.setdefault(PRODUCT, product)

for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)

Comment thread
gadgetmobile marked this conversation as resolved.
Outdated
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


@callback
def create_blebox_entities(product, async_add, entity_klass, entity_type):
"""Create entities from a BleBox product's features."""

entities = []
for feature in product.features[entity_type]:
entities.append(entity_klass(feature))

async_add(entities, True)


class BleBoxEntity(Entity):
"""Implements a common class for entities representing a BleBox feature."""

def __init__(self, feature):
"""Initialize a BleBox entity."""
self._feature = feature

@property
def name(self):
"""Return the internal entity name."""
return self._feature.full_name

@property
def unique_id(self):
"""Return a unique id."""
return self._feature.unique_id

async def async_update(self):
"""Update the entity state."""
try:
await self._feature.async_update()
except Error as ex:
_LOGGER.error("Updating '%s' failed: %s", self.name, ex)
Comment thread
bdraco marked this conversation as resolved.
Outdated

@property
def device_info(self):
"""Return device information for this entity."""
product = self._feature.product
return {
"identifiers": {(DOMAIN, product.unique_id)},
"name": product.name,
"manufacturer": product.brand,
"model": product.model,
"sw_version": product.firmware_version,
}
128 changes: 128 additions & 0 deletions homeassistant/components/blebox/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Config flow for BleBox devices integration."""
import logging

from blebox_uniapi.error import Error, UnsupportedBoxVersion
from blebox_uniapi.products import Products
from blebox_uniapi.session import ApiHost
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import (
ADDRESS_ALREADY_CONFIGURED,
CANNOT_CONNECT,
DEFAULT_HOST,
DEFAULT_PORT,
DEFAULT_SETUP_TIMEOUT,
DOMAIN,
UNKNOWN,
UNSUPPORTED_VERSION,
)

_LOGGER = logging.getLogger(__name__)


def host_port(data):
"""Return a list with host and port."""
return (data[CONF_HOST], data[CONF_PORT])


def create_schema(previous_input=None):
"""Create a schema with given values as default."""
if previous_input is not None:
host, port = host_port(previous_input)
else:
host = DEFAULT_HOST
port = DEFAULT_PORT

return vol.Schema(
{
vol.Required(CONF_HOST, default=host): str,
vol.Required(CONF_PORT, default=port): int,
}
)


LOG_MSG = {
UNSUPPORTED_VERSION: "Outdated firmware",
CANNOT_CONNECT: "Failed to identify device",
UNKNOWN: "Unknown error while identifying device",
}


class BleBoxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for BleBox devices."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

def __init__(self):
"""Initialize the BleBox config flow."""
self.device_config = {}

def handle_step_exception(
self, step, exception, schema, host, port, message_id, log_fn
):
"""Handle step exceptions."""

log_fn("%s at %s:%d (%s)", LOG_MSG[message_id], host, port, exception)

return self.async_show_form(
step_id="user",
data_schema=schema,
errors={"base": message_id},
description_placeholders={"address": f"{host}:{port}"},
)

async def async_step_user(self, user_input=None):
"""Handle initial user-triggered config step."""

hass = self.hass
schema = create_schema(user_input)

if user_input is None:
return self.async_show_form(
step_id="user",
data_schema=schema,
errors={},
description_placeholders={},
)

addr = host_port(user_input)

for entry in hass.config_entries.async_entries(DOMAIN):
if addr == host_port(entry.data):
host, port = addr
return self.async_abort(
reason=ADDRESS_ALREADY_CONFIGURED,
description_placeholders={"address": f"{host}:{port}"},
)

websession = async_get_clientsession(hass)
api_host = ApiHost(*addr, DEFAULT_SETUP_TIMEOUT, websession, hass.loop, _LOGGER)
Comment thread
gadgetmobile marked this conversation as resolved.

try:
product = await Products.async_from_host(api_host)

except UnsupportedBoxVersion as ex:
return self.handle_step_exception(
"user", ex, schema, *addr, UNSUPPORTED_VERSION, _LOGGER.debug
)

except Error as ex:
return self.handle_step_exception(
"user", ex, schema, *addr, CANNOT_CONNECT, _LOGGER.warning
)

except RuntimeError as ex:
return self.handle_step_exception(
"user", ex, schema, *addr, UNKNOWN, _LOGGER.error
)

# Check if configured but IP changed since
await self.async_set_unique_id(product.unique_id)
self._abort_if_unique_id_configured()

return self.async_create_entry(title=product.name, data=user_input)
45 changes: 45 additions & 0 deletions homeassistant/components/blebox/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Constants for the BleBox devices integration."""

from homeassistant.components.cover import (
DEVICE_CLASS_DOOR,
DEVICE_CLASS_GATE,
DEVICE_CLASS_SHUTTER,
STATE_CLOSED,
STATE_CLOSING,
STATE_OPEN,
STATE_OPENING,
)

DOMAIN = "blebox"
PRODUCT = "product"

DEFAULT_SETUP_TIMEOUT = 3

# translation strings
ADDRESS_ALREADY_CONFIGURED = "address_already_configured"
CANNOT_CONNECT = "cannot_connect"
UNSUPPORTED_VERSION = "unsupported_version"
UNKNOWN = "unknown"

BLEBOX_TO_HASS_DEVICE_CLASSES = {
"shutter": DEVICE_CLASS_SHUTTER,
"gatebox": DEVICE_CLASS_DOOR,
"gate": DEVICE_CLASS_GATE,
}

BLEBOX_TO_HASS_COVER_STATES = {
None: None,
0: STATE_CLOSING, # moving down
1: STATE_OPENING, # moving up
2: STATE_OPEN, # manually stopped
3: STATE_CLOSED, # lower limit
4: STATE_OPEN, # upper limit / open
# gateController
5: STATE_OPEN, # overload
6: STATE_OPEN, # motor failure
# 7 is not used
8: STATE_OPEN, # safety stop
}

DEFAULT_HOST = "192.168.0.2"
DEFAULT_PORT = 80
Loading