Skip to content

Commit

Permalink
First basic sensors
Browse files Browse the repository at this point in the history
  • Loading branch information
geertmeersman committed Apr 9, 2023
1 parent 38a0785 commit 8dcd3f8
Show file tree
Hide file tree
Showing 18 changed files with 1,332 additions and 1 deletion.
48 changes: 48 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml

target-version = "py310"

select = [
"B007", # Loop control variable {name} not used within loop body
"B014", # Exception handler with duplicate exception
"C", # complexity
"D", # docstrings
"E", # pycodestyle
"F", # pyflakes/autoflake
"ICN001", # import concentions; {name} should be imported as {asname}
"PGH004", # Use specific rule codes when using noqa
"PLC0414", # Useless import alias. Import alias does not rename original package.
"SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass
"SIM117", # Merge with-statements that use the same scope
"SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys()
"SIM201", # Use {left} != {right} instead of not {left} == {right}
"SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a}
"SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'.
"SIM401", # Use get from dict with default instead of an if block
"T20", # flake8-print
"TRY004", # Prefer TypeError exception for invalid type
"RUF006", # Store a reference to the return value of asyncio.create_task
"UP", # pyupgrade
"W", # pycodestyle
]

ignore = [
"D202", # No blank lines allowed after function docstring
"D203", # 1 blank line required before class docstring
"D213", # Multi-line docstring summary should start at the second line
"D404", # First word of the docstring should not be This
"D406", # Section name should end with a newline
"D407", # Section name underlining
"D411", # Missing blank line before section
"E501", # line too long
"E731", # do not assign a lambda expression, use a def
]

[flake8-pytest-style]
fixture-parentheses = false

[pyupgrade]
keep-runtime-typing = true

[mccabe]
max-complexity = 40
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

## Reporting a Vulnerability

If you found a vulnerability or suspect one to be present, please encode it [here](https://github.com/geertmeersman/telenet/security/advisories/new)
If you found a vulnerability or suspect one to be present, please encode it [here](https://github.com/geertmeersman/nexxtmove/security/advisories/new)
Each vulnerability will be treated with the needed care and priority
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.0.1
135 changes: 135 additions & 0 deletions custom_components/nexxtmove/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""Nexxtmove integration."""
from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD
from homeassistant.const import CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.helpers.update_coordinator import UpdateFailed
from requests.exceptions import ConnectionError

from .client import NexxtmoveClient
from .const import _LOGGER
from .const import COORDINATOR_UPDATE_INTERVAL
from .const import DOMAIN
from .const import PLATFORMS
from .exceptions import NexxtmoveException
from .exceptions import NexxtmoveServiceException
from .models import NexxtmoveItem
from .utils import log_debug


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Nexxtmove from a config entry."""
hass.data.setdefault(DOMAIN, {})

client = NexxtmoveClient(
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
)

dev_reg = dr.async_get(hass)
hass.data[DOMAIN][entry.entry_id] = coordinator = NexxtmoveDataUpdateCoordinator(
hass,
config_entry_id=entry.entry_id,
dev_reg=dev_reg,
client=client,
)

await coordinator.async_config_entry_first_refresh()

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


class NexxtmoveDataUpdateCoordinator(DataUpdateCoordinator):
"""Data update coordinator for Nexxtmove."""

data: list[NexxtmoveItem]
config_entry: ConfigEntry

def __init__(
self,
hass: HomeAssistant,
config_entry_id: str,
dev_reg: dr.DeviceRegistry,
client: NexxtmoveClient,
) -> None:
"""Initialize coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=COORDINATOR_UPDATE_INTERVAL,
)
self._config_entry_id = config_entry_id
self._device_registry = dev_reg
self.client = client
self.hass = hass

async def _async_update_data(self) -> dict | None:
"""Update data."""
try:
items = await self.hass.async_add_executor_job(
self.client.fetch_data
)
except ConnectionError as exception:
raise UpdateFailed(f"ConnectionError {exception}") from exception
except NexxtmoveServiceException as exception:
raise UpdateFailed(f"NexxtmoveServiceException {exception}") from exception
except NexxtmoveException as exception:
raise UpdateFailed(f"NexxtmoveException {exception}") from exception
except Exception as exception:
raise UpdateFailed(f"Exception {exception}") from exception

items: list[NexxtmoveItem] = items


current_items = {
list(device.identifiers)[0][1]
for device in dr.async_entries_for_config_entry(
self._device_registry, self._config_entry_id
)
}

if len(items) > 0:
fetched_items = {
str(items[item].device_key) for item in items
}
log_debug(
f"[init|NexxtmoveDataUpdateCoordinator|_async_update_data|fetched_items] {fetched_items}"
)
if stale_items := current_items - fetched_items:
for device_key in stale_items:
if device := self._device_registry.async_get_device(
{(DOMAIN, device_key)}
):
log_debug(
f"[init|NexxtmoveDataUpdateCoordinator|_async_update_data|async_remove_device] {device_key}",
True,
)
self._device_registry.async_remove_device(device.id)

# If there are new items, we should reload the config entry so we can
# create new devices and entities.
if self.data and fetched_items - {
str(self.data[item].device_key) for item in self.data
}:
# log_debug(f"[init|NexxtmoveDataUpdateCoordinator|_async_update_data|async_reload] {product.product_name}")
self.hass.async_create_task(
self.hass.config_entries.async_reload(self._config_entry_id)
)
return None
return items
return []
Loading

0 comments on commit 8dcd3f8

Please sign in to comment.