Skip to content
Merged
Show file tree
Hide file tree
Changes from 134 commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
18ab298
Initial import 0.0.2
pburgio Oct 26, 2023
fc35303
Fixes to URL, and removed commits
pburgio Oct 26, 2023
3c25e8f
Initial import 0.0.2
pburgio Oct 26, 2023
2b2022a
Fixes to URL, and removed commits
pburgio Oct 26, 2023
0f8be23
Added first test for iotty
pburgio Oct 29, 2023
f6c9bb6
First release
pburgio Oct 30, 2023
ae97d6f
Merge branch 'iotty' of https://iotty.visualstudio.com/iotty/_git/hom…
pburgio Oct 30, 2023
213478f
Merge branch 'dev' into iotty
pburgio Oct 31, 2023
2dfbcac
Merge branch 'dev' into iotty
pburgio Nov 5, 2023
fdbef16
Reviewers request #1
pburgio Nov 5, 2023
ac38fc5
Merge branch 'dev' into iotty
pburgio Nov 5, 2023
cd56f98
Merge branch 'iotty' of https://github.com/pburgio/homeassistant.core…
pburgio Nov 5, 2023
f27bb4b
Removed commmented code
pburgio Nov 5, 2023
938a9af
Some modifications
pburgio Nov 5, 2023
31a8d01
Modified REST EP for iotty CloudApi
pburgio Nov 6, 2023
fb65b75
Merge branch 'dev' into iotty
pburgio Nov 6, 2023
63c4b2b
Initial import 0.0.2
pburgio Oct 26, 2023
3525d64
Fixes to URL, and removed commits
pburgio Oct 26, 2023
2a3d779
Added first test for iotty
pburgio Oct 29, 2023
d6a02a4
First release
pburgio Oct 30, 2023
b5223a5
Rebased and resolved conflicts
pburgio Nov 6, 2023
014c3e5
Rebased and resolved conflicts
pburgio Nov 6, 2023
0903fbd
Reviewers request #1
pburgio Nov 5, 2023
c719025
Removed commmented code
pburgio Nov 5, 2023
a7b9348
Some modifications
pburgio Nov 5, 2023
fea0e1d
Modified REST EP for iotty CloudApi
pburgio Nov 6, 2023
4c77d3f
Removed empty entries in manifest.json
pburgio Nov 6, 2023
0895ce4
Added test_config_flow
pburgio Nov 6, 2023
102cc67
Resolved conflicts
pburgio Nov 6, 2023
852e63e
Merge branch 'home-assistant:dev' into iotty
pburgio Nov 7, 2023
263b056
Merge branch 'dev' into iotty
pburgio Nov 7, 2023
2446c28
Fix as requested by @edenhaus
pburgio Nov 7, 2023
a8171e6
Merge branch 'dev' into iotty
pburgio Nov 7, 2023
42df504
Merge branch 'home-assistant:dev' into iotty
pburgio Nov 20, 2023
a4bbf4d
Merge branch 'iotty' of https://github.com/pburgio/homeassistant.core…
pburgio Nov 20, 2023
fa97a76
Merge branch 'dev' into iotty
pburgio Nov 23, 2023
41d1d5a
Merge branch 'iotty' of https://github.com/pburgio/homeassistant.core…
pburgio Nov 23, 2023
ab6afb1
Added test_init
pburgio Nov 23, 2023
1adc04d
Removed comments, added one assert
pburgio Nov 23, 2023
a137732
Added TEST_CONFIG_FLOW
pburgio Nov 27, 2023
def435b
Added test for STORE_ENTITY
pburgio Nov 27, 2023
2378530
Increased code coverage
pburgio Dec 1, 2023
a2c2e37
Full coverage for api.py
pburgio Dec 1, 2023
c72dec7
Added tests for switch component
pburgio Dec 1, 2023
6b4ac81
Merge branch 'dev' into iotty
pburgio Dec 6, 2023
f0d1c82
Merge branch 'dev' into iotty
pburgio Dec 6, 2023
fd9d6b9
Merge branch 'iotty' of https://github.com/pburgio/homeassistant.core…
pburgio Dec 6, 2023
235d95d
Converted INFO logs onto DEBUG logs
pburgio Dec 7, 2023
57f93c7
Merge branch 'dev' into iotty
pburgio Dec 7, 2023
d9c1e7c
Merge branch 'iotty' of https://github.com/pburgio/homeassistant.core…
pburgio Dec 7, 2023
e131b4f
Merge branch 'dev' into iotty
pburgio Dec 13, 2023
e5657db
Merge branch 'dev' into iotty
pburgio Dec 13, 2023
ddd9155
Merge branch 'dev' into iotty
pburgio Dec 18, 2023
aa7d87c
Merge branch 'dev' into iotty
pburgio Dec 19, 2023
f6e8269
Merge branch 'dev' into iotty
pburgio Jan 18, 2024
2406cea
Merge branch 'home-assistant:dev' into iotty
pburgio Jan 18, 2024
45993ed
Removed .gitignore from commits
pburgio Jan 18, 2024
ef16522
Modifications to SWITCH.PY
pburgio Jan 18, 2024
860cb78
Merge branch 'dev' into iotty
pburgio Jan 19, 2024
fb520ad
Initial import 0.0.2
pburgio Oct 26, 2023
b57182b
Fixes to URL, and removed commits
pburgio Oct 26, 2023
a6d9f43
Added first test for iotty
pburgio Oct 29, 2023
fd14b35
First release
pburgio Oct 30, 2023
9954782
Rebased and resolved conflicts
pburgio Nov 6, 2023
b3e4195
Fixed conflicts
pburgio Oct 30, 2023
dc47223
Reviewers request #1
pburgio Nov 5, 2023
f3bf64b
Removed commmented code
pburgio Nov 5, 2023
5df2e39
Some modifications
pburgio Nov 5, 2023
85c85a7
Modified REST EP for iotty CloudApi
pburgio Nov 6, 2023
7924c27
Removed empty entries in manifest.json
pburgio Nov 6, 2023
40dd193
Added test_config_flow
pburgio Nov 6, 2023
90c5cb5
Some modifications
pburgio Nov 5, 2023
2874d26
Fix as requested by @edenhaus
pburgio Nov 7, 2023
0d67c1c
Added test_init
pburgio Nov 23, 2023
6c9054c
Removed comments, added one assert
pburgio Nov 23, 2023
d8ac598
Added TEST_CONFIG_FLOW
pburgio Nov 27, 2023
d5f447a
Added test for STORE_ENTITY
pburgio Nov 27, 2023
202bdd7
Increased code coverage
pburgio Dec 1, 2023
1c273e1
Full coverage for api.py
pburgio Dec 1, 2023
4c30c1a
Added tests for switch component
pburgio Dec 1, 2023
eec1675
Converted INFO logs onto DEBUG logs
pburgio Dec 7, 2023
76b20bb
Removed .gitignore from commits
pburgio Jan 18, 2024
65317e0
Modifications to SWITCH.PY
pburgio Jan 18, 2024
c34e841
Fixed tests for SWITCH
pburgio Jan 19, 2024
f364332
Resolved conflicts
pburgio Jan 19, 2024
92b5cd9
First working implementation of Coordinator
pburgio Jan 25, 2024
3c07035
Increased code coverage
pburgio Jan 25, 2024
291c89d
Full code coverage
pburgio Jan 26, 2024
7d68719
Merge branch 'dev' into iotty
pburgio Jan 27, 2024
dec43e9
Missing a line in testing
pburgio Jan 27, 2024
4953f33
Merge branch 'dev' into iotty
pburgio Feb 3, 2024
3ab508c
Update homeassistant/components/iotty/__init__.py
pburgio Feb 3, 2024
d338e19
Update homeassistant/components/iotty/__init__.py
pburgio Feb 3, 2024
02551d6
Modified coordinator as per request by edenhaus
pburgio Feb 3, 2024
e67903c
Merge branch 'dev' into iotty
pburgio Mar 14, 2024
3df1fa7
use coordinator entities for switches
shapournemati-iotty Apr 23, 2024
14ecf78
move platforms to constants
shapournemati-iotty Apr 23, 2024
9b56504
Merge branch 'dev' into iotty
pburgio Apr 24, 2024
6bc5d16
fix whitespace with ruff-format
shapournemati-iotty May 9, 2024
2bf2cdd
Merge branch 'dev' into iotty
emontnemery May 14, 2024
6371d37
correct iotty entry in application_credentials list
shapournemati-iotty May 15, 2024
86b29da
Merge branch 'dev' into iotty
pburgio May 15, 2024
84d9977
minor style improvements
shapournemati-iotty May 16, 2024
0be59ce
refactor function name
shapournemati-iotty May 16, 2024
a3a32e0
Merge branch 'dev' into iotty
pburgio May 16, 2024
d2989f2
handle new and deleted devices
shapournemati-iotty May 28, 2024
fe9da43
Merge branch 'dev' into iotty
pburgio May 29, 2024
4497c2c
Merge branch 'dev' into iotty
shapournemati-iotty Jun 5, 2024
d3b6cc5
improve code for adding devices after first initialization
shapournemati-iotty Jun 6, 2024
56cf2cb
use typed config entry instead of adding known devices to hass.data
shapournemati-iotty Jun 6, 2024
43e79c1
Merge branch 'dev' into iotty
pburgio Jun 7, 2024
271e9e7
improve iotty entity removal
shapournemati-iotty Jun 17, 2024
a47a715
test listeners update cycle
shapournemati-iotty Jun 19, 2024
b02eea8
handle iotty as devices and not only as entities
shapournemati-iotty Jul 1, 2024
02acc82
Merge branch 'dev' into iotty
shapournemati-iotty Jul 1, 2024
01f8759
fix test typing for mock config entry
shapournemati-iotty Jul 1, 2024
7278fec
test with fewer mocks for an integration test style opposed to the pr…
shapournemati-iotty Jul 2, 2024
3328374
remove useless tests and add more integration style tests
shapournemati-iotty Jul 3, 2024
ce6e24e
check if device_to_remove is None
shapournemati-iotty Jul 4, 2024
4eac992
integration style tests for turning switches on and off
shapournemati-iotty Jul 4, 2024
b6cbcff
remove redundant coordinator tests
shapournemati-iotty Jul 4, 2024
4f14518
check device status after issuing command in tests
shapournemati-iotty Jul 4, 2024
91cdb09
Merge branch 'dev' into iotty
shapournemati-iotty Jul 4, 2024
e87cead
remove unused fixtures
shapournemati-iotty Jul 5, 2024
43c2662
add strict typing for iotty
shapournemati-iotty Jul 5, 2024
055f775
Merge branch 'iotty' of https://github.com/pburgio/homeassistant.core…
shapournemati-iotty Jul 5, 2024
eb195a3
additional asserts and named snapshots in tests
shapournemati-iotty Jul 5, 2024
f07da58
fix mypy issues after enabling strict typing
shapournemati-iotty Jul 5, 2024
b90f820
upgrade iottycloud version to 0.1.3
shapournemati-iotty Jul 5, 2024
383fc90
move coordinator to runtime_data
shapournemati-iotty Jul 5, 2024
c618e69
remove entity name
shapournemati-iotty Jul 5, 2024
c4ac6bb
fix typing issues
shapournemati-iotty Jul 8, 2024
5e87f2a
coding style fixes
shapournemati-iotty Jul 10, 2024
1399c04
improve tests coding style and assertion targets
shapournemati-iotty Jul 10, 2024
967e260
test edge cases when apis are not working
shapournemati-iotty Jul 18, 2024
0b17b2c
Merge branch 'dev' into iotty
pburgio Jul 18, 2024
299d884
improve tests comments and assertions
shapournemati-iotty Jul 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ homeassistant.components.integration.*
homeassistant.components.intent.*
homeassistant.components.intent_script.*
homeassistant.components.ios.*
homeassistant.components.iotty.*
homeassistant.components.ipp.*
homeassistant.components.iqvia.*
homeassistant.components.islamic_prayer_times.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,8 @@ build.json @home-assistant/supervisor
/tests/components/ios/ @robbiet480
/homeassistant/components/iotawatt/ @gtdiehl @jyavenard
/tests/components/iotawatt/ @gtdiehl @jyavenard
/homeassistant/components/iotty/ @pburgio
/tests/components/iotty/ @pburgio
/homeassistant/components/iperf3/ @rohankapoorcom
/homeassistant/components/ipma/ @dgomes
/tests/components/ipma/ @dgomes
Expand Down
56 changes: 56 additions & 0 deletions homeassistant/components/iotty/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""The iotty integration."""

from __future__ import annotations

from dataclasses import dataclass
import logging

from iottycloud.device import Device

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_entry_oauth2_flow import (
OAuth2Session,
async_get_config_entry_implementation,
)

from . import coordinator

_LOGGER = logging.getLogger(__name__)

PLATFORMS: list[Platform] = [Platform.SWITCH]

type IottyConfigEntry = ConfigEntry[IottyConfigEntryData]


@dataclass
class IottyConfigEntryData:
"""Contains config entry data for iotty."""

known_devices: set[Device]
coordinator: coordinator.IottyDataUpdateCoordinator


async def async_setup_entry(hass: HomeAssistant, entry: IottyConfigEntry) -> bool:
"""Set up iotty from a config entry."""
_LOGGER.debug("async_setup_entry entry_id=%s", entry.entry_id)

implementation = await async_get_config_entry_implementation(hass, entry)
session = OAuth2Session(hass, entry, implementation)

data_update_coordinator = coordinator.IottyDataUpdateCoordinator(
hass, entry, session
)

entry.runtime_data = IottyConfigEntryData(set(), data_update_coordinator)

await data_update_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."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
40 changes: 40 additions & 0 deletions homeassistant/components/iotty/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""API for iotty bound to Home Assistant OAuth."""

from __future__ import annotations

from typing import Any

from aiohttp import ClientSession
from iottycloud.cloudapi import CloudApi

from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow

OAUTH2_CLIENT_ID = "hass-iotty"
IOTTYAPI_BASE = "https://homeassistant.iotty.com/"


class IottyProxy(CloudApi):
"""Provide iotty authentication tied to an OAuth2 based config entry."""

def __init__(
self,
hass: HomeAssistant,
websession: ClientSession,
oauth_session: config_entry_oauth2_flow.OAuth2Session,
) -> None:
"""Initialize iotty auth."""

super().__init__(websession, IOTTYAPI_BASE, OAUTH2_CLIENT_ID)
if oauth_session is None:
raise ValueError("oauth_session")
self._oauth_session = oauth_session
self._hass = hass

async def async_get_access_token(self) -> Any:
"""Return a valid access token."""

if not self._oauth_session.valid_token:
await self._oauth_session.async_ensure_token_valid()

return self._oauth_session.token["access_token"]
17 changes: 17 additions & 0 deletions homeassistant/components/iotty/application_credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Application credentials platform for iotty."""

from __future__ import annotations

from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant

OAUTH2_AUTHORIZE = "https://auth.iotty.com/.auth/oauth2/login"
OAUTH2_TOKEN = "https://auth.iotty.com/.auth/oauth2/token"


async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer:
"""Return authorization server."""
return AuthorizationServer(
authorize_url=OAUTH2_AUTHORIZE,
token_url=OAUTH2_TOKEN,
)
22 changes: 22 additions & 0 deletions homeassistant/components/iotty/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Config flow for iotty."""

from __future__ import annotations

import logging

from homeassistant.helpers import config_entry_oauth2_flow

from .const import DOMAIN


class OAuth2FlowHandler(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
):
"""Config flow to handle iotty OAuth2 authentication."""

DOMAIN = DOMAIN

@property
def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)
5 changes: 5 additions & 0 deletions homeassistant/components/iotty/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Constants for the iotty integration."""

from __future__ import annotations

DOMAIN = "iotty"
108 changes: 108 additions & 0 deletions homeassistant/components/iotty/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""DataUpdateCoordinator for iotty."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import timedelta
import logging

from iottycloud.device import Device
from iottycloud.verbs import RESULT, STATUS

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, device_registry as dr
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from . import api
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

UPDATE_INTERVAL = timedelta(seconds=30)


@dataclass
class IottyData:
"""iotty data stored in the DataUpdateCoordinator."""

devices: list[Device]


class IottyDataUpdateCoordinator(DataUpdateCoordinator[IottyData]):
"""Class to manage fetching Iotty data."""

config_entry: ConfigEntry
_entities: dict[str, Entity]
_devices: list[Device]
_device_registry: dr.DeviceRegistry

def __init__(
self, hass: HomeAssistant, entry: ConfigEntry, session: OAuth2Session
) -> None:
"""Initialize the coordinator."""
_LOGGER.debug("Initializing iotty data update coordinator")

super().__init__(
hass,
_LOGGER,
name=f"{DOMAIN}_coordinator",
update_interval=UPDATE_INTERVAL,
)

self.config_entry = entry
self._entities = {}
self._devices = []
self.iotty = api.IottyProxy(
hass, aiohttp_client.async_get_clientsession(hass), session
)
self._device_registry = dr.async_get(hass)

async def async_config_entry_first_refresh(self) -> None:
"""Override the first refresh to also fetch iotty devices list."""
_LOGGER.debug("Fetching devices list from iottyCloud")
self._devices = await self.iotty.get_devices()
_LOGGER.debug("There are %d devices", len(self._devices))

await super().async_config_entry_first_refresh()

async def _async_update_data(self) -> IottyData:
"""Fetch data from iottyCloud device."""
_LOGGER.debug("Fetching devices status from iottyCloud")

current_devices = await self.iotty.get_devices()

removed_devices = [
d
for d in self._devices
if not any(x.device_id == d.device_id for x in current_devices)
]

for removed_device in removed_devices:
device_to_remove = self._device_registry.async_get_device(
{(DOMAIN, removed_device.device_id)}
)
if device_to_remove is not None:
self._device_registry.async_remove_device(device_to_remove.id)

self._devices = current_devices

for device in self._devices:
res = await self.iotty.get_status(device.device_id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you make one request per device, I suggest creating a coordinator per device.
So you don't need to override async_config_entry_first_refresh to fetch all devices

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I'm not sure if that's what you meant, but I have used the coordinator entity! Let me know if that satisfies your request

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What @edenhaus meant is that it seems the coordinator is not useful, because you make one API call for each device. Instead, each device could have its own coordinator.
I think the direction here depends a bit on the plan for future PR which adds functionality, where it may be more useful to keep all data in a single coordinator.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. Actually, as you already said at the end, our future plans would actually benefit from having all the info in one coordinator. Moreover, we are also considering using a single API call to get all the devices info at once, and then cycle through them to get the updated info, which cannot be done using one coordinator per device.

json = res.get(RESULT, {})
if (
not isinstance(res, dict)
or RESULT not in res
or not isinstance(json := res[RESULT], dict)
or not (status := json.get(STATUS))
):
_LOGGER.warning("Unable to read status for device %s", device.device_id)
else:
_LOGGER.debug(
"Retrieved status: '%s' for device %s", status, device.device_id
)
device.update_status(status)

return IottyData(self._devices)
11 changes: 11 additions & 0 deletions homeassistant/components/iotty/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"domain": "iotty",
"name": "iotty",
"codeowners": ["@pburgio"],
"config_flow": true,
"dependencies": ["application_credentials"],
"documentation": "https://www.home-assistant.io/integrations/iotty",
"integration_type": "device",
"iot_class": "cloud_polling",
"requirements": ["iottycloud==0.1.3"]
}
21 changes: 21 additions & 0 deletions homeassistant/components/iotty/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"config": {
"step": {
"pick_implementation": {
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]"
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
}
}
}
Loading