1
mirror of https://github.com/home-assistant/core synced 2024-09-25 00:41:32 +02:00

Use own CoAP lib and support for multicast updates (#42718)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Shay Levy 2020-11-02 17:46:34 +02:00 committed by GitHub
parent e5dee965f1
commit f45075eeb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 33 deletions

View File

@ -387,7 +387,7 @@ homeassistant/components/seven_segments/* @fabaff
homeassistant/components/seventeentrack/* @bachya
homeassistant/components/sharkiq/* @ajmarks
homeassistant/components/shell_command/* @home-assistant/core
homeassistant/components/shelly/* @balloob @bieniu
homeassistant/components/shelly/* @balloob @bieniu @thecode
homeassistant/components/shiftr/* @fabaff
homeassistant/components/shodan/* @fabaff
homeassistant/components/sighthound/* @robmarkcole

View File

@ -2,8 +2,8 @@
import asyncio
from datetime import timedelta
import logging
from socket import gethostbyname
import aiocoap
import aioshelly
import async_timeout
@ -14,44 +14,59 @@ from homeassistant.const import (
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client, device_registry, update_coordinator
from homeassistant.helpers import (
aiohttp_client,
device_registry,
singleton,
update_coordinator,
)
from .const import COAP_CONTEXT, DATA_CONFIG_ENTRY, DOMAIN
from .const import DATA_CONFIG_ENTRY, DOMAIN
PLATFORMS = ["binary_sensor", "cover", "light", "sensor", "switch"]
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Shelly component."""
hass.data[DOMAIN] = {DATA_CONFIG_ENTRY: {}}
hass.data[DOMAIN][COAP_CONTEXT] = await aiocoap.Context.create_client_context()
@singleton.singleton("shelly_coap")
async def get_coap_context(hass):
"""Get CoAP context to be used in all Shelly devices."""
context = aioshelly.COAP()
await context.initialize()
async def shutdown_listener(*_):
"""Home Assistant shutdown listener."""
await hass.data[DOMAIN][COAP_CONTEXT].shutdown()
@callback
def shutdown_listener(ev):
context.close()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shutdown_listener)
return context
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Shelly component."""
hass.data[DOMAIN] = {DATA_CONFIG_ENTRY: {}}
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Shelly from a config entry."""
temperature_unit = "C" if hass.config.units.is_metric else "F"
ip_address = await hass.async_add_executor_job(gethostbyname, entry.data[CONF_HOST])
options = aioshelly.ConnectionOptions(
entry.data[CONF_HOST],
ip_address,
entry.data.get(CONF_USERNAME),
entry.data.get(CONF_PASSWORD),
temperature_unit,
)
coap_context = hass.data[DOMAIN][COAP_CONTEXT]
coap_context = await get_coap_context(hass)
try:
async with async_timeout.timeout(10):
async with async_timeout.timeout(5):
device = await aioshelly.Device.create(
aiohttp_client.async_get_clientsession(hass),
coap_context,
@ -78,23 +93,35 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
def __init__(self, hass, entry, device: aioshelly.Device):
"""Initialize the Shelly device wrapper."""
sleep_mode = device.settings.get("sleep_mode")
if sleep_mode:
sleep_period = sleep_mode["period"]
if sleep_mode["unit"] == "h":
sleep_period *= 60 # hours to minutes
update_interval = 1.2 * sleep_period * 60 # minutes to seconds
else:
update_interval = 2 * device.settings["coiot"]["update_period"]
super().__init__(
hass,
_LOGGER,
name=device.settings["name"] or device.settings["device"]["hostname"],
update_interval=timedelta(seconds=5),
update_interval=timedelta(seconds=update_interval),
)
self.hass = hass
self.entry = entry
self.device = device
self.device.subscribe_updates(self.async_set_updated_data)
async def _async_update_data(self):
"""Fetch data."""
try:
async with async_timeout.timeout(5):
return await self.device.update()
except (aiocoap.error.Error, OSError) as err:
except OSError as err:
raise update_coordinator.UpdateFailed("Error fetching data") from err
@property
@ -122,6 +149,10 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
sw_version=self.device.settings["fw"],
)
def shutdown(self):
"""Shutdown the wrapper."""
self.device.shutdown()
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
@ -134,6 +165,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
)
)
if unload_ok:
hass.data[DOMAIN][DATA_CONFIG_ENTRY].pop(entry.entry_id)
hass.data[DOMAIN][DATA_CONFIG_ENTRY].pop(entry.entry_id).shutdown()
return unload_ok

View File

@ -1,8 +1,8 @@
"""Config flow for Shelly integration."""
import asyncio
import logging
from socket import gethostbyname
import aiocoap
import aiohttp
import aioshelly
import async_timeout
@ -17,6 +17,7 @@ from homeassistant.const import (
)
from homeassistant.helpers import aiohttp_client
from .__init__ import get_coap_context
from .const import DOMAIN # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
@ -37,10 +38,13 @@ async def validate_input(hass: core.HomeAssistant, host, data):
Data has the keys from DATA_SCHEMA with values provided by the user.
"""
ip_address = await hass.async_add_executor_job(gethostbyname, host)
options = aioshelly.ConnectionOptions(
host, data.get(CONF_USERNAME), data.get(CONF_PASSWORD)
ip_address, data.get(CONF_USERNAME), data.get(CONF_PASSWORD)
)
coap_context = await aiocoap.Context.create_client_context()
coap_context = await get_coap_context(hass)
async with async_timeout.timeout(5):
device = await aioshelly.Device.create(
aiohttp_client.async_get_clientsession(hass),
@ -48,7 +52,7 @@ async def validate_input(hass: core.HomeAssistant, host, data):
options,
)
await coap_context.shutdown()
device.shutdown()
# Return info that you want to store in the config entry.
return {

View File

@ -1,5 +1,4 @@
"""Constants for the Shelly integration."""
COAP_CONTEXT = "coap_context"
DATA_CONFIG_ENTRY = "config_entry"
DOMAIN = "shelly"

View File

@ -3,7 +3,7 @@
"name": "Shelly",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/shelly",
"requirements": ["aioshelly==0.4.0"],
"requirements": ["aioshelly==0.5.0"],
"zeroconf": [{ "type": "_http._tcp.local.", "name": "shelly*" }],
"codeowners": ["@balloob", "@bieniu"]
"codeowners": ["@balloob", "@bieniu", "@thecode"]
}

View File

@ -221,7 +221,7 @@ aiopvpc==2.0.2
aiopylgtv==0.3.3
# homeassistant.components.shelly
aioshelly==0.4.0
aioshelly==0.5.0
# homeassistant.components.switcher_kis
aioswitcher==1.2.1

View File

@ -137,7 +137,7 @@ aiopvpc==2.0.2
aiopylgtv==0.3.3
# homeassistant.components.shelly
aioshelly==0.4.0
aioshelly==0.5.0
# homeassistant.components.switcher_kis
aioswitcher==1.2.1

View File

@ -43,7 +43,6 @@ async def test_form(hass):
"aioshelly.Device.create",
new=AsyncMock(
return_value=Mock(
shutdown=AsyncMock(),
settings=MOCK_SETTINGS,
)
),
@ -88,7 +87,6 @@ async def test_title_without_name_and_prefix(hass):
"aioshelly.Device.create",
new=AsyncMock(
return_value=Mock(
shutdown=AsyncMock(),
settings=settings,
)
),
@ -137,7 +135,6 @@ async def test_form_auth(hass):
"aioshelly.Device.create",
new=AsyncMock(
return_value=Mock(
shutdown=AsyncMock(),
settings=MOCK_SETTINGS,
)
),
@ -309,7 +306,6 @@ async def test_zeroconf(hass):
"aioshelly.Device.create",
new=AsyncMock(
return_value=Mock(
shutdown=AsyncMock(),
settings=MOCK_SETTINGS,
)
),
@ -466,7 +462,6 @@ async def test_zeroconf_require_auth(hass):
"aioshelly.Device.create",
new=AsyncMock(
return_value=Mock(
shutdown=AsyncMock(),
settings=MOCK_SETTINGS,
)
),