diff --git a/custom_components/iotawatt/__init__.py b/custom_components/iotawatt/__init__.py index ccd5523..8b4d746 100644 --- a/custom_components/iotawatt/__init__.py +++ b/custom_components/iotawatt/__init__.py @@ -1,8 +1,8 @@ """The iotawatt integration.""" -import asyncio +from datetime import timedelta import logging +from typing import Dict, List -from datetime import timedelta from httpx import AsyncClient from iotawattpy.iotawatt import Iotawatt import voluptuous as vol @@ -10,23 +10,21 @@ from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_SCAN_INTERVAL -from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryNotReady, PlatformNotReady +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, - UpdateFailed, ) from .const import ( + COORDINATOR, DEFAULT_ICON, DEFAULT_SCAN_INTERVAL, DOMAIN, - COORDINATOR, IOTAWATT_API, SIGNAL_ADD_DEVICE, - SIGNAL_DELETE_DEVICE, ) CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA) @@ -88,21 +86,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): class IotawattUpdater(DataUpdateCoordinator): - """Class to manage fetching update data from the IoTaWatt Energy Device""" + """Class to manage fetching update data from the IoTaWatt Energy Device.""" def __init__(self, hass: HomeAssistant, api: str, name: str, update_interval: int): + """Initialize IotaWattUpdater object.""" self.api = api - self.sensorlist = {} + self.sensorlist: Dict[str, List[str]] = {} super().__init__( hass=hass, logger=_LOGGER, name=name, - update_interval=timedelta(seconds=update_interval) + update_interval=timedelta(seconds=update_interval), ) async def _async_update_data(self): - """Fetch sensors from IoTaWatt device""" + """Fetch sensors from IoTaWatt device.""" await self.api.update() sensors = self.api.getSensors() @@ -119,6 +118,7 @@ async def _async_update_data(self): return sensors + class IotaWattEntity(CoordinatorEntity, SensorEntity): """Defines the base IoTaWatt Energy Device entity.""" @@ -134,7 +134,7 @@ def __init__(self, coordinator: IotawattUpdater, entity, mac_address, name): @property def unique_id(self) -> str: """Return a unique, Home Assistant friendly identifier for this entity.""" - return self._mac_addr + return self._mac_address @property def name(self) -> str: @@ -144,4 +144,4 @@ def name(self) -> str: @property def icon(self): """Return the icon for the entity.""" - return self._icon + return self._icon \ No newline at end of file diff --git a/custom_components/iotawatt/config_flow.py b/custom_components/iotawatt/config_flow.py index fd3135b..a81fb05 100644 --- a/custom_components/iotawatt/config_flow.py +++ b/custom_components/iotawatt/config_flow.py @@ -1,14 +1,14 @@ """Config flow for iotawatt integration.""" -import httpx import json import logging +import httpx from httpx import AsyncClient from iotawattpy.iotawatt import Iotawatt import voluptuous as vol from homeassistant import config_entries, core, exceptions -from homeassistant.const import CONF_NAME, CONF_HOST, CONF_USERNAME, CONF_PASSWORD +from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME from .const import DOMAIN @@ -22,30 +22,13 @@ ) -class PlaceholderHub: - """Placeholder class to make tests pass. - - TODO Remove this placeholder class and replace with things from your PyPI package. - """ - - def __init__(self, name): - """Initialize.""" - self.name = name - - async def authenticate(self, username, password) -> bool: - """Test if we can authenticate with the host.""" - return True - - async def validate_input(hass: core.HomeAssistant, data): """Validate the user input allows us to connect. Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. """ session = AsyncClient() - iotawatt = Iotawatt( - data["name"], data["host"], session - ) + iotawatt = Iotawatt(data["name"], data["host"], session) try: is_connected = await iotawatt.connect() _LOGGER.debug("isConnected: %s", is_connected) @@ -81,7 +64,7 @@ async def async_step_user(self, user_input=None): self._data.update(user_input) try: - info = await validate_input(self.hass, user_input) + await validate_input(self.hass, user_input) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: @@ -90,27 +73,25 @@ async def async_step_user(self, user_input=None): _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - return self.async_create_entry(title=info["title"], data=user_input) + return self.async_create_entry(title=self._data["name"], data=user_input) return self.async_show_form( step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors ) async def async_step_auth(self, user_input=None): + """Authenticate user if authentication is enabled on the IoTaWatt device.""" data_schema = vol.Schema( { vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, } ) - + _LOGGER.debug("Data: %s", self._data) if user_input is None: - _LOGGER.debug("fdsfsf") - return self.async_show_form( - step_id="auth", data_schema=data_schema - ) + return self.async_show_form(step_id="auth", data_schema=data_schema) self._data.update(user_input) - return self.async_create_entry(title="title", data=self._data) + return self.async_create_entry(title=self._data["name"], data=self._data) class CannotConnect(exceptions.HomeAssistantError): @@ -118,4 +99,4 @@ class CannotConnect(exceptions.HomeAssistantError): class InvalidAuth(exceptions.HomeAssistantError): - """Error to indicate there is invalid auth.""" + """Error to indicate there is invalid auth.""" \ No newline at end of file diff --git a/custom_components/iotawatt/manifest.json b/custom_components/iotawatt/manifest.json index 4ab4964..ec11c6c 100644 --- a/custom_components/iotawatt/manifest.json +++ b/custom_components/iotawatt/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/iotawatt", "issue_tracker": "https://github.com/gtdiehl/iotawatt_ha", "requirements": [ - "iotawattpy==0.0.6" + "iotawattpy==0.0.7" ], "ssdp": [], "zeroconf": [], @@ -14,5 +14,5 @@ "codeowners": [ "@gtdiehl" ], - "version": "0.0.3" + "version": "0.0.4" } diff --git a/custom_components/iotawatt/sensor.py b/custom_components/iotawatt/sensor.py index 02d17d4..4d8ff35 100644 --- a/custom_components/iotawatt/sensor.py +++ b/custom_components/iotawatt/sensor.py @@ -1,38 +1,20 @@ """Support for IoTaWatt Energy monitor.""" -from datetime import timedelta, datetime -from functools import partial import logging +from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT from homeassistant.const import ( - POWER_WATT, - ENERGY_WATT_HOUR, - ELECTRIC_POTENTIAL_VOLT, - DEVICE_CLASS_POWER, DEVICE_CLASS_ENERGY, - DEVICE_CLASS_VOLTAGE -) - -from homeassistant.core import callback -from homeassistant.components.sensor import ( - STATE_CLASS_MEASUREMENT, -) -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, - async_dispatcher_send, -) -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ELECTRIC_POTENTIAL_VOLT, + ENERGY_WATT_HOUR, + POWER_WATT, ) +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import dt -from . import IotaWattEntity, IotawattUpdater -from .const import ( - COORDINATOR, - DOMAIN, - SIGNAL_ADD_DEVICE, - SIGNAL_DELETE_DEVICE, -) +from . import IotaWattEntity +from .const import COORDINATOR, DOMAIN, SIGNAL_ADD_DEVICE _LOGGER = logging.getLogger(__name__) @@ -61,7 +43,10 @@ async def async_new_entities(sensor_info): name = sensor_info["name"] entity = IotaWattSensor( - coordinator=coordinator, entity=ent, mac_address=hub_mac_address, name=name, + coordinator=coordinator, + entity=ent, + mac_address=hub_mac_address, + name=name, ) entities = [entity] async_add_entities(entities) @@ -98,7 +83,6 @@ def __init__(self, coordinator, entity, mac_address, name): else: self._attr_unit_of_measurement = unit - @property def device_state_attributes(self): """Return the state attributes of the device.""" @@ -107,10 +91,7 @@ def device_state_attributes(self): else: channel = "N/A" - attrs = { - "type": self._io_type, - "channel": channel - } + attrs = {"type": self._io_type, "channel": channel} return attrs @@ -141,4 +122,4 @@ def name(self): @property def unique_id(self) -> str: """Return the Uniqie ID for the sensor.""" - return self.coordinator.data["sensors"][self._ent].getSensorID() + return self.coordinator.data["sensors"][self._ent].getSensorID() \ No newline at end of file diff --git a/custom_components/iotawatt/strings.json b/custom_components/iotawatt/strings.json index 93a1190..13f3832 100644 --- a/custom_components/iotawatt/strings.json +++ b/custom_components/iotawatt/strings.json @@ -12,7 +12,8 @@ "data": { "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]" - } + }, + "description": "The IoTawatt device requires authentication. Please enter the username and password and click the Submit button." } }, "error": { diff --git a/custom_components/iotawatt/translations/en.json b/custom_components/iotawatt/translations/en.json index 53862a7..70db1d5 100644 --- a/custom_components/iotawatt/translations/en.json +++ b/custom_components/iotawatt/translations/en.json @@ -13,7 +13,8 @@ "data": { "password": "Password", "username": "Username" - } + }, + "description": "The IoTawatt device requires authentication. Please enter the username and password and click the Submit button." }, "user": { "data": { diff --git a/hacs.json b/hacs.json index 6ecfb10..0d29dae 100644 --- a/hacs.json +++ b/hacs.json @@ -2,6 +2,6 @@ "name": "IoTaWatt", "render_readme": true, "domains": ["sensor"], - "homeassistant": "2020.12.1", + "homeassistant": "2021.8.0", "iot_class": ["Local Polling"] } \ No newline at end of file diff --git a/info.md b/info.md new file mode 100644 index 0000000..82394b9 --- /dev/null +++ b/info.md @@ -0,0 +1,41 @@ +{% if prerelease %} + +# This is a Beta version! + +--- + +{% endif %} + +{% if installed and version_installed != selected_tag %} + +# Changes as compared to your installed version: + +{% if version_installed.replace(".","") | int < 4 %} + +## Breaking Changes + +{% if version_installed.replace(".","") | int < 4 %} + +- Integration only works with Home Assistant 2021.8.0 and greater + {% endif %} + +{% if version_installed.replace(".","") | int < 4 %} + +### Changes + +{% if version_installed.replace(".","") | int < 4 %} + +- Add support for Energy view + {% endif %} + +## Features + +## Bugfixes + +# Home Assistant Integration for IoTaWatt + +This project provides [IoTaWatt](https://iotawatt.com/) support through a +custom integration for Home Assistant. It creates entites for each input and +output present in IoTaWatt. + +The integration is available in HACS.