Skip to content

Commit b5c4b61

Browse files
authored
Merge pull request #3 from Crewski/0.1.3
Add access token refresh and HA device
2 parents 73bf391 + 37c2c6a commit b5c4b61

File tree

8 files changed

+230
-83
lines changed

8 files changed

+230
-83
lines changed

custom_components/pumpspy_ha/__init__.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,32 @@
44
import logging
55
import async_timeout
66

7+
from homeassistant.helpers.device_registry import DeviceEntry
8+
79
from .pumpspy import Pumpspy
810

911
from homeassistant.config_entries import ConfigEntry
10-
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_USERNAME, Platform
12+
from homeassistant.const import (
13+
CONF_ACCESS_TOKEN,
14+
CONF_PASSWORD,
15+
CONF_USERNAME,
16+
Platform,
17+
)
1118
from homeassistant.core import HomeAssistant
1219
from homeassistant.helpers.update_coordinator import (
1320
DataUpdateCoordinator,
1421
)
1522

1623
# from homeassistant.exceptions import ConfigEntryAuthFailed
1724

18-
from .const import CONF_DEVICE_NAME, CONF_DEVICEID, CONF_REFRESH_TOKEN, DOMAIN
25+
26+
from .const import (
27+
CONF_DEVICE_NAME,
28+
CONF_DEVICEID,
29+
CONF_REFRESH_TOKEN,
30+
DOMAIN,
31+
MANUFACTURER,
32+
)
1933

2034
# TODO List the platforms that you want to support.
2135
# For your initial PR, limit it to 1 platform.
@@ -33,13 +47,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
3347
entry.data[CONF_DEVICEID],
3448
entry.data[CONF_USERNAME],
3549
entry.data[CONF_DEVICE_NAME],
50+
entry.data[CONF_PASSWORD],
3651
)
3752
coordinator = PumpspyCoordinator(hass, api)
53+
await coordinator.async_config_entry_first_refresh()
54+
3855
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
3956
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
4057

41-
await coordinator.async_config_entry_first_refresh()
42-
4358
return True
4459

4560

@@ -51,6 +66,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
5166
return unload_ok
5267

5368

69+
async def async_remove_config_entry_device(
70+
hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry
71+
) -> bool:
72+
"""Remove a config entry from a device."""
73+
return True
74+
75+
5476
class PumpspyCoordinator(DataUpdateCoordinator):
5577
"""Pumpspy coordinator."""
5678

@@ -76,21 +98,21 @@ async def _async_update_data(self):
7698
# Note: asyncio.TimeoutError and aiohttp.ClientError are already
7799
# handled by the data update coordinator.
78100
data = {}
79-
async with async_timeout.timeout(10):
101+
async with async_timeout.timeout(30):
80102
data["current"] = await self.api.fetch_current_data()
81-
async with async_timeout.timeout(10):
103+
async with async_timeout.timeout(30):
82104
data["main_daily"] = await self.api.fetch_interval_data("ac", "day")
83-
async with async_timeout.timeout(10):
105+
async with async_timeout.timeout(30):
84106
data["backup_daily"] = await self.api.fetch_interval_data("dc", "day")
85-
async with async_timeout.timeout(10):
107+
async with async_timeout.timeout(30):
86108
data["main_monthly"] = await self.api.fetch_interval_data("ac", "month")
87-
async with async_timeout.timeout(10):
109+
async with async_timeout.timeout(30):
88110
data["backup_monthly"] = await self.api.fetch_interval_data(
89111
"dc", "month"
90112
)
91-
async with async_timeout.timeout(10):
113+
async with async_timeout.timeout(30):
92114
data["main_weekly"] = await self.api.fetch_interval_data("ac", "week")
93-
async with async_timeout.timeout(10):
115+
async with async_timeout.timeout(30):
94116
data["backup_weekly"] = await self.api.fetch_interval_data("dc", "week")
95117
return data
96118
except Exception as err:

custom_components/pumpspy_ha/binary_sensor.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
"""Platform for binary sensor integration."""
2+
from __future__ import annotations
3+
from typing import Any
4+
from collections.abc import Mapping
25

6+
from homeassistant.helpers.entity import EntityCategory
37

8+
from .entity import PumpspyEntity
49
from homeassistant.core import callback
510
from homeassistant.helpers.update_coordinator import CoordinatorEntity
611
from homeassistant.components.binary_sensor import (
@@ -44,19 +49,22 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
4449
async_add_entities(new_devices)
4550

4651

47-
class AlertBinarySensor(CoordinatorEntity, BinarySensorEntity):
52+
class AlertBinarySensor(PumpspyEntity, BinarySensorEntity):
4853
"""Alert Binary Sensor"""
4954

5055
def __init__(self, coordinator, alert: str):
5156
"""Initialize the sensor."""
52-
super().__init__(coordinator)
57+
# super().__init__(coordinator)
58+
self.coordinator = coordinator
59+
self.coordinator_context = object()
5360
self._available = True
5461
self._alert = alert
5562
self._attr_device_class = (
5663
BinarySensorDeviceClass.CONNECTIVITY
5764
if alert == ALERT_CONNECTED
5865
else BinarySensorDeviceClass.PROBLEM
5966
)
67+
self._attr_entity_category = EntityCategory.DIAGNOSTIC
6068

6169
device_info = self.coordinator.api.get_device_info()
6270

@@ -65,16 +73,27 @@ def __init__(self, coordinator, alert: str):
6573
f'{device_info[CONF_DEVICE_NAME]} {alert.replace("_", " ").title()}'
6674
)
6775

68-
@callback
69-
def _handle_coordinator_update(self) -> None:
70-
"""Handle updated data from the coordinator."""
71-
if self.coordinator.data is None:
72-
return
76+
# @callback
77+
# def _handle_coordinator_update(self) -> None:
78+
# """Handle updated data from the coordinator."""
79+
# if self.coordinator.data is None:
80+
# return
81+
# val = self.coordinator.data["current"][0][self._alert]["state"]
82+
# if self._alert == ALERT_BATTERY_CHARGE_LEVEL:
83+
# val = not bool(val)
84+
# self._attr_is_on = val
85+
# self._attr_extra_state_attributes = {
86+
# "message": self.coordinator.data["current"][0][self._alert]["message"]
87+
# }
88+
# self.async_write_ha_state()
89+
90+
@property
91+
def is_on(self) -> bool | None:
7392
val = self.coordinator.data["current"][0][self._alert]["state"]
7493
if self._alert == ALERT_BATTERY_CHARGE_LEVEL:
7594
val = not bool(val)
76-
self._attr_is_on = val
77-
self._attr_extra_state_attributes = {
78-
"message": self.coordinator.data["current"][0][self._alert]["message"]
79-
}
80-
self.async_write_ha_state()
95+
return val
96+
97+
@property
98+
def extra_state_attributes(self) -> Mapping[str, Any] | None:
99+
return {"message": self.coordinator.data["current"][0][self._alert]["message"]}

custom_components/pumpspy_ha/config_flow.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
from homeassistant.helpers import selector
1414

1515
from homeassistant.const import (
16+
CONF_DEVICE_ID,
1617
CONF_USERNAME,
18+
CONF_ACCESS_TOKEN,
1719
CONF_PASSWORD,
1820
)
1921

custom_components/pumpspy_ha/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55

66
DOMAIN = "pumpspy_ha"
7+
MANUFACTURER = "Pumpspy"
78

89
BASE_URL = "http://www.pumpspy.com:8081"
910
TOKEN_URL = "/oauth/token"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Base Entity for Pumpspy."""
2+
from __future__ import annotations
3+
4+
from homeassistant.helpers.entity import DeviceInfo
5+
from . import PumpspyCoordinator
6+
from homeassistant.helpers.update_coordinator import CoordinatorEntity
7+
from .const import DOMAIN, MANUFACTURER
8+
9+
10+
class PumpspyEntity(CoordinatorEntity[PumpspyCoordinator]):
11+
"""Defines a base Pumpspy entity."""
12+
13+
def __init__(self, *, coordinator: PumpspyCoordinator) -> None:
14+
"""Initialize the entity."""
15+
super().__init__(coordinator)
16+
17+
@property
18+
def device_info(self) -> DeviceInfo | None:
19+
try:
20+
return DeviceInfo(
21+
identifiers={(DOMAIN, self.coordinator.data["current"][0]["deviceid"])},
22+
name=self.coordinator.data["current"][0]["user_nickname"],
23+
manufacturer=MANUFACTURER,
24+
model=self.coordinator.data["current"][0]["device_types_name"],
25+
hw_version=self.coordinator.data["current"][0]["hardware_rev"],
26+
sw_version=self.coordinator.data["current"][0]["firmware_rev"],
27+
)
28+
except TypeError:
29+
return None

custom_components/pumpspy_ha/manifest.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
"domain": "pumpspy_ha",
33
"name": "Pumpspy-HA",
44
"config_flow": true,
5-
"documentation": "https://github.com/Crewski/Pumpspy-HA",
6-
"issue_tracker": "https://github.com/Crewski/Pumpspy-HA/issues",
5+
"documentation": "https://www.home-assistant.io/integrations/pumpspy_ha",
76
"requirements": [],
87
"ssdp": [],
98
"zeroconf": [],

custom_components/pumpspy_ha/pumpspy.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ def __init__(self, hass: HomeAssistant) -> None:
3131
"""Initialize."""
3232
self.hass = hass
3333
self.username = None
34+
self.password = None
3435
self.access_token = None
3536
self.refresh_token = None
3637
self.deviceid = None
3738
self.device_name = None
3839

39-
async def get_token(self, password: str) -> None:
40+
async def get_token(self) -> None:
4041
"""Get bearer token"""
4142

4243
# GET TOKEN
@@ -47,7 +48,7 @@ async def get_token(self, password: str) -> None:
4748
data = {
4849
"grant_type": CONF_PASSWORD,
4950
CONF_USERNAME: self.username,
50-
CONF_PASSWORD: password,
51+
CONF_PASSWORD: self.password,
5152
}
5253
session = aiohttp_client.async_get_clientsession(self.hass)
5354
async with session.post(
@@ -79,8 +80,8 @@ async def get_uid(self) -> None: # GET UID
7980
async def get_locations(self, username: str, password: str):
8081
"""Get the available locations"""
8182
self.username = username
82-
83-
await self.get_token(password)
83+
self.password = password
84+
await self.get_token()
8485
uid = await self.get_uid()
8586
session = aiohttp_client.async_get_clientsession(self.hass)
8687
async with session.get(
@@ -110,6 +111,9 @@ async def fetch_current_data(self):
110111
) as resp:
111112
if resp.status == 200:
112113
return await resp.json()
114+
elif resp.status == 401:
115+
await self.get_token()
116+
self.fetch_current_data()
113117
else:
114118
raise CannotConnect
115119

@@ -125,6 +129,9 @@ async def fetch_interval_data(self, type: str, interval: str):
125129
) as resp:
126130
if resp.status == 200:
127131
return await resp.json()
132+
elif resp.status == 401:
133+
await self.get_token()
134+
self.fetch_current_data()
128135
else:
129136
raise CannotConnect
130137

@@ -152,6 +159,7 @@ def get_variables(self):
152159
CONF_DEVICEID: self.deviceid,
153160
CONF_USERNAME: self.username,
154161
CONF_DEVICE_NAME: self.device_name,
162+
CONF_PASSWORD: self.password,
155163
}
156164

157165
def set_variables(
@@ -161,13 +169,15 @@ def set_variables(
161169
deviceid,
162170
username: str,
163171
device_name: str,
172+
password: str,
164173
):
165174
"""Set all the variables for a new instance"""
166175
self.username = username
167176
self.access_token = access_token
168177
self.refresh_token = refresh_token
169178
self.deviceid = deviceid
170179
self.device_name = device_name
180+
self.password = password
171181

172182

173183
class CannotConnect(HomeAssistantError):

0 commit comments

Comments
 (0)