Remove sampleclient (#45)

This commit is contained in:
Joakim Sørensen 2020-11-14 15:27:54 +01:00 committed by GitHub
parent bf76923975
commit 668bd9d0bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 129 additions and 47 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.pythonPath": "/usr/local/bin/python"
}

View File

@ -4,8 +4,6 @@ The component and platforms in this repository are not meant to be used by a
user, but as a "blueprint" that custom component developers can build user, but as a "blueprint" that custom component developers can build
upon, to make more awesome stuff. upon, to make more awesome stuff.
This blueprint uses ['sampleclient'](https://github.com/ludeeus/sampleclient) to simulate what you actually might use in your integration.
HAVE FUN! 😎 HAVE FUN! 😎
## Why? ## Why?
@ -29,6 +27,7 @@ File | Purpose
`.vscode/tasks.json` | Tasks for the devcontainer. `.vscode/tasks.json` | Tasks for the devcontainer.
`custom_components/blueprint/translations/*` | [Translation files.](https://developers.home-assistant.io/docs/internationalization/custom_integration) `custom_components/blueprint/translations/*` | [Translation files.](https://developers.home-assistant.io/docs/internationalization/custom_integration)
`custom_components/blueprint/__init__.py` | The component file for the integration. `custom_components/blueprint/__init__.py` | The component file for the integration.
`custom_components/blueprint/api.py` | This is a sample API client.
`custom_components/blueprint/binary_sensor.py` | Binary sensor platform for the integration. `custom_components/blueprint/binary_sensor.py` | Binary sensor platform for the integration.
`custom_components/blueprint/config_flow.py` | Config flow file, this adds the UI configuration possibilities. `custom_components/blueprint/config_flow.py` | Config flow file, this adds the UI configuration possibilities.
`custom_components/blueprint/const.py` | A file to hold shared variables/constants for the entire integration. `custom_components/blueprint/const.py` | A file to hold shared variables/constants for the entire integration.
@ -104,10 +103,11 @@ Platform | Description
Using your HA configuration directory (folder) as a starting point you should now also have this: Using your HA configuration directory (folder) as a starting point you should now also have this:
```text ```text
custom_components/blueprint/.translations/en.json custom_components/blueprint/translations/en.json
custom_components/blueprint/.translations/nb.json custom_components/blueprint/translations/nb.json
custom_components/blueprint/.translations/sensor.nb.json custom_components/blueprint/translations/sensor.nb.json
custom_components/blueprint/__init__.py custom_components/blueprint/__init__.py
custom_components/blueprint/api.py
custom_components/blueprint/binary_sensor.py custom_components/blueprint/binary_sensor.py
custom_components/blueprint/config_flow.py custom_components/blueprint/config_flow.py
custom_components/blueprint/const.py custom_components/blueprint/const.py

View File

@ -11,10 +11,12 @@ import logging
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Config, HomeAssistant from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from sampleclient.client import Client
from custom_components.blueprint.const import ( from .api import BlueprintApiClient
from .const import (
CONF_PASSWORD, CONF_PASSWORD,
CONF_USERNAME, CONF_USERNAME,
DOMAIN, DOMAIN,
@ -24,7 +26,7 @@ from custom_components.blueprint.const import (
SCAN_INTERVAL = timedelta(seconds=30) SCAN_INTERVAL = timedelta(seconds=30)
_LOGGER = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__package__)
async def async_setup(hass: HomeAssistant, config: Config): async def async_setup(hass: HomeAssistant, config: Config):
@ -41,9 +43,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
username = entry.data.get(CONF_USERNAME) username = entry.data.get(CONF_USERNAME)
password = entry.data.get(CONF_PASSWORD) password = entry.data.get(CONF_PASSWORD)
coordinator = BlueprintDataUpdateCoordinator( session = async_get_clientsession(hass)
hass, username=username, password=password client = BlueprintApiClient(username, password, session)
)
coordinator = BlueprintDataUpdateCoordinator(hass, client=client)
await coordinator.async_refresh() await coordinator.async_refresh()
if not coordinator.last_update_success: if not coordinator.last_update_success:
@ -65,9 +68,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
class BlueprintDataUpdateCoordinator(DataUpdateCoordinator): class BlueprintDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the API.""" """Class to manage fetching data from the API."""
def __init__(self, hass, username, password): def __init__(self, hass: HomeAssistant, client: BlueprintApiClient) -> None:
"""Initialize.""" """Initialize."""
self.api = Client(username, password) self.api: BlueprintApiClient = client
self.platforms = [] self.platforms = []
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)
@ -75,13 +78,12 @@ class BlueprintDataUpdateCoordinator(DataUpdateCoordinator):
async def _async_update_data(self): async def _async_update_data(self):
"""Update data via library.""" """Update data via library."""
try: try:
data = await self.api.async_get_data() return await self.api.async_get_data()
return data.get("data", {})
except Exception as exception: except Exception as exception:
raise UpdateFailed(exception) raise UpdateFailed() from exception
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Handle removal of an entry.""" """Handle removal of an entry."""
coordinator = hass.data[DOMAIN][entry.entry_id] coordinator = hass.data[DOMAIN][entry.entry_id]
unloaded = all( unloaded = all(
@ -99,7 +101,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
return unloaded return unloaded
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload config entry.""" """Reload config entry."""
await async_unload_entry(hass, entry) await async_unload_entry(hass, entry)
await async_setup_entry(hass, entry) await async_setup_entry(hass, entry)

View File

@ -0,0 +1,75 @@
"""Sample API Client."""
import logging
import asyncio
import socket
from typing import Optional
import aiohttp
import async_timeout
TIMEOUT = 10
_LOGGER: logging.Logger = logging.getLogger(__package__)
HEADERS = {"Content-type": "application/json; charset=UTF-8"}
class BlueprintApiClient:
def __init__(
self, username: str, password: str, session: aiohttp.ClientSession
) -> None:
"""Sample API Client."""
self._username = username
self._passeword = password
self._session = session
async def async_get_data(self) -> dict:
"""Get data from the API."""
url = "https://jsonplaceholder.typicode.com/posts/1"
return await self.api_wrapper("get", url)
async def async_set_title(self, value: str) -> None:
"""Get data from the API."""
url = "https://jsonplaceholder.typicode.com/posts/1"
await self.api_wrapper("patch", url, data={"title": value}, headers=HEADERS)
async def api_wrapper(
self, method: str, url: str, data: dict = {}, headers: dict = {}
) -> dict:
"""Get information from the API."""
try:
async with async_timeout.timeout(TIMEOUT, loop=asyncio.get_event_loop()):
if method == "get":
response = await self._session.get(url, headers=headers)
return await response.json()
elif method == "put":
await self._session.put(url, headers=headers, json=data)
elif method == "patch":
await self._session.patch(url, headers=headers, json=data)
elif method == "post":
await self._session.post(url, headers=headers, json=data)
except asyncio.TimeoutError as exception:
_LOGGER.error(
"Timeout error fetching information from %s - %s",
url,
exception,
)
except (KeyError, TypeError) as exception:
_LOGGER.error(
"Error parsing information from %s - %s",
url,
exception,
)
except (aiohttp.ClientError, socket.gaierror) as exception:
_LOGGER.error(
"Error fetching information from %s - %s",
url,
exception,
)
except Exception as exception: # pylint: disable=broad-except
_LOGGER.error("Something really wrong happend! - %s", exception)

View File

@ -1,13 +1,13 @@
"""Binary sensor platform for blueprint.""" """Binary sensor platform for blueprint."""
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
from custom_components.blueprint.const import ( from .const import (
BINARY_SENSOR, BINARY_SENSOR,
BINARY_SENSOR_DEVICE_CLASS, BINARY_SENSOR_DEVICE_CLASS,
DEFAULT_NAME, DEFAULT_NAME,
DOMAIN, DOMAIN,
) )
from custom_components.blueprint.entity import BlueprintEntity from .entity import BlueprintEntity
async def async_setup_entry(hass, entry, async_add_devices): async def async_setup_entry(hass, entry, async_add_devices):
@ -32,4 +32,4 @@ class BlueprintBinarySensor(BlueprintEntity, BinarySensorDevice):
@property @property
def is_on(self): def is_on(self):
"""Return true if the binary_sensor is on.""" """Return true if the binary_sensor is on."""
return self.coordinator.data.get("bool_on", False) return self.coordinator.data.get("title", "") == "foo"

View File

@ -1,10 +1,11 @@
"""Adds config flow for Blueprint.""" """Adds config flow for Blueprint."""
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.core import callback from homeassistant.core import callback
from sampleclient.client import Client from homeassistant.helpers.aiohttp_client import async_create_clientsession
import voluptuous as vol import voluptuous as vol
from custom_components.blueprint.const import ( # pylint: disable=unused-import from .api import BlueprintApiClient
from .const import (
CONF_PASSWORD, CONF_PASSWORD,
CONF_USERNAME, CONF_USERNAME,
DOMAIN, DOMAIN,
@ -22,9 +23,7 @@ class BlueprintFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Initialize.""" """Initialize."""
self._errors = {} self._errors = {}
async def async_step_user( async def async_step_user(self, user_input=None):
self, user_input=None # pylint: disable=bad-continuation
):
"""Handle a flow initialized by the user.""" """Handle a flow initialized by the user."""
self._errors = {} self._errors = {}
@ -65,7 +64,8 @@ class BlueprintFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
async def _test_credentials(self, username, password): async def _test_credentials(self, username, password):
"""Return true if credentials is valid.""" """Return true if credentials is valid."""
try: try:
client = Client(username, password) session = async_create_clientsession(self.hass)
client = BlueprintApiClient(username, password, session)
await client.async_get_data() await client.async_get_data()
return True return True
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except

View File

@ -4,7 +4,7 @@ NAME = "Blueprint"
DOMAIN = "blueprint" DOMAIN = "blueprint"
DOMAIN_DATA = f"{DOMAIN}_data" DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "0.0.1" VERSION = "0.0.1"
ATTRIBUTION = "Data provided by http://jsonplaceholder.typicode.com/"
ISSUE_URL = "https://github.com/custom-components/blueprint/issues" ISSUE_URL = "https://github.com/custom-components/blueprint/issues"
# Icons # Icons

View File

@ -1,7 +1,7 @@
"""BlueprintEntity class""" """BlueprintEntity class"""
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from custom_components.blueprint.const import DOMAIN, NAME, VERSION from .const import DOMAIN, NAME, VERSION, ATTRIBUTION
class BlueprintEntity(CoordinatorEntity): class BlueprintEntity(CoordinatorEntity):
@ -27,6 +27,7 @@ class BlueprintEntity(CoordinatorEntity):
def device_state_attributes(self): def device_state_attributes(self):
"""Return the state attributes.""" """Return the state attributes."""
return { return {
"time": str(self.coordinator.data.get("time")), "attribution": ATTRIBUTION,
"static": self.coordinator.data.get("static"), "id": str(self.coordinator.data.get("id")),
"integration": DOMAIN,
} }

View File

@ -2,12 +2,11 @@
"domain": "blueprint", "domain": "blueprint",
"name": "Blueprint", "name": "Blueprint",
"documentation": "https://github.com/custom-components/blueprint", "documentation": "https://github.com/custom-components/blueprint",
"issue_tracker": "https://github.com/custom-components/blueprint/issues",
"dependencies": [], "dependencies": [],
"config_flow": true, "config_flow": true,
"codeowners": [ "codeowners": [
"@ludeeus" "@ludeeus"
], ],
"requirements": [ "requirements": []
"sampleclient"
]
} }

View File

@ -1,6 +1,6 @@
"""Sensor platform for blueprint.""" """Sensor platform for blueprint."""
from custom_components.blueprint.const import DEFAULT_NAME, DOMAIN, ICON, SENSOR from .const import DEFAULT_NAME, DOMAIN, ICON, SENSOR
from custom_components.blueprint.entity import BlueprintEntity from .entity import BlueprintEntity
async def async_setup_entry(hass, entry, async_add_devices): async def async_setup_entry(hass, entry, async_add_devices):
@ -20,7 +20,7 @@ class BlueprintSensor(BlueprintEntity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.coordinator.data.get("static") return self.coordinator.data.get("body")
@property @property
def icon(self): def icon(self):

View File

@ -1,8 +1,8 @@
"""Switch platform for blueprint.""" """Switch platform for blueprint."""
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchEntity
from custom_components.blueprint.const import DEFAULT_NAME, DOMAIN, ICON, SWITCH from .const import DEFAULT_NAME, DOMAIN, ICON, SWITCH
from custom_components.blueprint.entity import BlueprintEntity from .entity import BlueprintEntity
async def async_setup_entry(hass, entry, async_add_devices): async def async_setup_entry(hass, entry, async_add_devices):
@ -11,17 +11,17 @@ async def async_setup_entry(hass, entry, async_add_devices):
async_add_devices([BlueprintBinarySwitch(coordinator, entry)]) async_add_devices([BlueprintBinarySwitch(coordinator, entry)])
class BlueprintBinarySwitch(BlueprintEntity, SwitchDevice): class BlueprintBinarySwitch(BlueprintEntity, SwitchEntity):
"""blueprint switch class.""" """blueprint switch class."""
async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument
"""Turn on the switch.""" """Turn on the switch."""
await self.coordinator.api.async_change_something(True) await self.coordinator.api.async_set_title("bar")
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument
"""Turn off the switch.""" """Turn off the switch."""
await self.coordinator.api.async_change_something(False) await self.coordinator.api.async_set_title("foo")
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
@property @property
@ -37,4 +37,4 @@ class BlueprintBinarySwitch(BlueprintEntity, SwitchDevice):
@property @property
def is_on(self): def is_on(self):
"""Return true if the switch is on.""" """Return true if the switch is on."""
return self.coordinator.api.something return self.coordinator.data.get("title", "") == "foo"

View File

@ -1,11 +1,11 @@
{ {
"name": "Blueprint", "name": "Blueprint",
"hacs": "0.24.0", "hacs": "1.6.0",
"domains": [ "domains": [
"binary_sensor", "binary_sensor",
"sensor", "sensor",
"switch" "switch"
], ],
"iot_class": "Cloud Polling", "iot_class": "Cloud Polling",
"homeassistant": "0.115.0" "homeassistant": "0.118.0"
} }

View File

@ -16,7 +16,7 @@ _Component to integrate with [blueprint][blueprint]._
Platform | Description Platform | Description
-- | -- -- | --
`binary_sensor` | Show something `True` or `False`. `binary_sensor` | Show something `True` or `False`.
`sensor` | Show info from blueprint API. `sensor` | Show info from API.
`switch` | Switch something `True` or `False`. `switch` | Switch something `True` or `False`.
![example][exampleimg] ![example][exampleimg]