Skip to content

Commit

Permalink
Remove sampleclient (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludeeus authored Nov 14, 2020
1 parent bf76923 commit 668bd9d
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.pythonPath": "/usr/local/bin/python"
}
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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! 😎

## Why?
Expand All @@ -29,6 +27,7 @@ File | Purpose
`.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/__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/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.
Expand Down Expand Up @@ -104,10 +103,11 @@ Platform | Description
Using your HA configuration directory (folder) as a starting point you should now also have this:

```text
custom_components/blueprint/.translations/en.json
custom_components/blueprint/.translations/nb.json
custom_components/blueprint/.translations/sensor.nb.json
custom_components/blueprint/translations/en.json
custom_components/blueprint/translations/nb.json
custom_components/blueprint/translations/sensor.nb.json
custom_components/blueprint/__init__.py
custom_components/blueprint/api.py
custom_components/blueprint/binary_sensor.py
custom_components/blueprint/config_flow.py
custom_components/blueprint/const.py
Expand Down
28 changes: 15 additions & 13 deletions custom_components/blueprint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
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_USERNAME,
DOMAIN,
Expand All @@ -24,7 +26,7 @@

SCAN_INTERVAL = timedelta(seconds=30)

_LOGGER = logging.getLogger(__name__)
_LOGGER: logging.Logger = logging.getLogger(__package__)


async def async_setup(hass: HomeAssistant, config: Config):
Expand All @@ -41,9 +43,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
username = entry.data.get(CONF_USERNAME)
password = entry.data.get(CONF_PASSWORD)

coordinator = BlueprintDataUpdateCoordinator(
hass, username=username, password=password
)
session = async_get_clientsession(hass)
client = BlueprintApiClient(username, password, session)

coordinator = BlueprintDataUpdateCoordinator(hass, client=client)
await coordinator.async_refresh()

if not coordinator.last_update_success:
Expand All @@ -65,23 +68,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
class BlueprintDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the API."""

def __init__(self, hass, username, password):
def __init__(self, hass: HomeAssistant, client: BlueprintApiClient) -> None:
"""Initialize."""
self.api = Client(username, password)
self.api: BlueprintApiClient = client
self.platforms = []

super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)

async def _async_update_data(self):
"""Update data via library."""
try:
data = await self.api.async_get_data()
return data.get("data", {})
return await self.api.async_get_data()
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."""
coordinator = hass.data[DOMAIN][entry.entry_id]
unloaded = all(
Expand All @@ -99,7 +101,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
return unloaded


async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload config entry."""
await async_unload_entry(hass, entry)
await async_setup_entry(hass, entry)
75 changes: 75 additions & 0 deletions custom_components/blueprint/api.py
Original file line number Diff line number Diff line change
@@ -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)
6 changes: 3 additions & 3 deletions custom_components/blueprint/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""Binary sensor platform for blueprint."""
from homeassistant.components.binary_sensor import BinarySensorDevice

from custom_components.blueprint.const import (
from .const import (
BINARY_SENSOR,
BINARY_SENSOR_DEVICE_CLASS,
DEFAULT_NAME,
DOMAIN,
)
from custom_components.blueprint.entity import BlueprintEntity
from .entity import BlueprintEntity


async def async_setup_entry(hass, entry, async_add_devices):
Expand All @@ -32,4 +32,4 @@ def device_class(self):
@property
def is_on(self):
"""Return true if the binary_sensor is on."""
return self.coordinator.data.get("bool_on", False)
return self.coordinator.data.get("title", "") == "foo"
12 changes: 6 additions & 6 deletions custom_components/blueprint/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Adds config flow for Blueprint."""
from homeassistant import config_entries
from homeassistant.core import callback
from sampleclient.client import Client
from homeassistant.helpers.aiohttp_client import async_create_clientsession
import voluptuous as vol

from custom_components.blueprint.const import ( # pylint: disable=unused-import
from .api import BlueprintApiClient
from .const import (
CONF_PASSWORD,
CONF_USERNAME,
DOMAIN,
Expand All @@ -22,9 +23,7 @@ def __init__(self):
"""Initialize."""
self._errors = {}

async def async_step_user(
self, user_input=None # pylint: disable=bad-continuation
):
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
self._errors = {}

Expand Down Expand Up @@ -65,7 +64,8 @@ async def _show_config_form(self, user_input): # pylint: disable=unused-argumen
async def _test_credentials(self, username, password):
"""Return true if credentials is valid."""
try:
client = Client(username, password)
session = async_create_clientsession(self.hass)
client = BlueprintApiClient(username, password, session)
await client.async_get_data()
return True
except Exception: # pylint: disable=broad-except
Expand Down
2 changes: 1 addition & 1 deletion custom_components/blueprint/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
DOMAIN = "blueprint"
DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "0.0.1"

ATTRIBUTION = "Data provided by http://jsonplaceholder.typicode.com/"
ISSUE_URL = "https://github.com/custom-components/blueprint/issues"

# Icons
Expand Down
7 changes: 4 additions & 3 deletions custom_components/blueprint/entity.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""BlueprintEntity class"""
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):
Expand All @@ -27,6 +27,7 @@ def device_info(self):
def device_state_attributes(self):
"""Return the state attributes."""
return {
"time": str(self.coordinator.data.get("time")),
"static": self.coordinator.data.get("static"),
"attribution": ATTRIBUTION,
"id": str(self.coordinator.data.get("id")),
"integration": DOMAIN,
}
5 changes: 2 additions & 3 deletions custom_components/blueprint/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
"domain": "blueprint",
"name": "Blueprint",
"documentation": "https://github.com/custom-components/blueprint",
"issue_tracker": "https://github.com/custom-components/blueprint/issues",
"dependencies": [],
"config_flow": true,
"codeowners": [
"@ludeeus"
],
"requirements": [
"sampleclient"
]
"requirements": []
}
6 changes: 3 additions & 3 deletions custom_components/blueprint/sensor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Sensor platform for blueprint."""
from custom_components.blueprint.const import DEFAULT_NAME, DOMAIN, ICON, SENSOR
from custom_components.blueprint.entity import BlueprintEntity
from .const import DEFAULT_NAME, DOMAIN, ICON, SENSOR
from .entity import BlueprintEntity


async def async_setup_entry(hass, entry, async_add_devices):
Expand All @@ -20,7 +20,7 @@ def name(self):
@property
def state(self):
"""Return the state of the sensor."""
return self.coordinator.data.get("static")
return self.coordinator.data.get("body")

@property
def icon(self):
Expand Down
14 changes: 7 additions & 7 deletions custom_components/blueprint/switch.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""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 custom_components.blueprint.entity import BlueprintEntity
from .const import DEFAULT_NAME, DOMAIN, ICON, SWITCH
from .entity import BlueprintEntity


async def async_setup_entry(hass, entry, async_add_devices):
Expand All @@ -11,17 +11,17 @@ async def async_setup_entry(hass, entry, async_add_devices):
async_add_devices([BlueprintBinarySwitch(coordinator, entry)])


class BlueprintBinarySwitch(BlueprintEntity, SwitchDevice):
class BlueprintBinarySwitch(BlueprintEntity, SwitchEntity):
"""blueprint switch class."""

async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument
"""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()

async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument
"""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()

@property
Expand All @@ -37,4 +37,4 @@ def icon(self):
@property
def is_on(self):
"""Return true if the switch is on."""
return self.coordinator.api.something
return self.coordinator.data.get("title", "") == "foo"
4 changes: 2 additions & 2 deletions hacs.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "Blueprint",
"hacs": "0.24.0",
"hacs": "1.6.0",
"domains": [
"binary_sensor",
"sensor",
"switch"
],
"iot_class": "Cloud Polling",
"homeassistant": "0.115.0"
"homeassistant": "0.118.0"
}
2 changes: 1 addition & 1 deletion info.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ _Component to integrate with [blueprint][blueprint]._
Platform | Description
-- | --
`binary_sensor` | Show something `True` or `False`.
`sensor` | Show info from blueprint API.
`sensor` | Show info from API.
`switch` | Switch something `True` or `False`.

![example][exampleimg]
Expand Down

0 comments on commit 668bd9d

Please sign in to comment.