Skip to content

Commit 928fda4

Browse files
authored
Revert "feature/sync-measurement-units (#138)" (#142)
This reverts commit d39c4de.
1 parent e00f1cd commit 928fda4

26 files changed

Lines changed: 394 additions & 1734 deletions

custom_components/petlibro/config_flow.py

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,21 @@
1010
import voluptuous as vol
1111

1212
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult, OptionsFlow
13-
from homeassistant.const import CONF_API_TOKEN, CONF_EMAIL, CONF_PASSWORD, CONF_REGION, Platform
13+
from homeassistant.const import CONF_API_TOKEN, CONF_EMAIL, CONF_PASSWORD, CONF_REGION
1414
from homeassistant.core import callback
1515
from homeassistant.data_entry_flow import section
1616
from homeassistant.helpers.aiohttp_client import async_get_clientsession
1717
from homeassistant.helpers.selector import selector
18-
from homeassistant.helpers.entity_registry import async_get as get_entity_registry
1918

2019
from .api import PetLibroAPI
2120
from .const import (
2221
DEFAULT_FEED,
2322
DEFAULT_WATER,
2423
DEFAULT_WEIGHT,
25-
ROUNDING_RULES,
2624
DOMAIN,
27-
APIKey as API,
25+
CommonAPIKeys as API,
2826
Gender,
29-
Unit,
27+
UnitTypes,
3028
)
3129
from .exceptions import PetLibroCannotConnect, PetLibroInvalidAuth
3230
from .hub import PetLibroHub
@@ -203,54 +201,25 @@ async def async_step_account_settings(
203201
return self.async_abort(reason="account_update_nomember")
204202

205203
user_input = user_input or {}
206-
if user_input:
207-
update_setting_temp = user_input.pop("measurement_unit", {})
208-
update_info_temp = user_input.copy()
209-
user_input.update(**update_setting_temp)
210-
update_all_units = update_setting_temp.pop("update_all_units", False)
211-
204+
if user_input:
212205
update_setting = self.collect_updates(
213206
fields=(API.FEED_UNIT, API.WATER_UNIT, API.WEIGHT_UNIT),
214-
user_input=update_setting_temp,
215-
enum_cls=Unit,
207+
user_input=user_input.pop("measurement_unit", {}),
208+
enum_cls=UnitTypes,
216209
)
217210

218211
update_info = self.collect_updates(
219212
fields=(API.NICKNAME, API.GENDER),
220-
user_input=update_info_temp,
213+
user_input=user_input,
221214
special={
222215
API.NICKNAME: lambda v: v or "",
223216
API.GENDER: lambda v: self.validate_enum(API.GENDER, v, Gender),
224217
},
225218
)
226-
227-
if update_setting or update_all_units:
228-
registry = get_entity_registry(self.hass)
229-
for unit_type in self.hub.unit_sensor_unique_ids:
230-
unit = (input if isinstance(input := update_setting.get(unit_type), Unit)
231-
else Unit(input) if input else getattr(self.member, unit_type, None))
232-
if (unit_type not in update_setting or not unit or not unit.device_class) and not update_all_units:
233-
continue
234-
_LOGGER.debug("Updating %s sensor entities", unit_type)
235-
if update_all_units and unit_type == API.FEED_UNIT:
236-
target_units = {"weight": unit if unit.device_class == "weight" else Unit.GRAMS,
237-
"volume": unit if unit.device_class == "volume" else Unit.MILLILITERS}
238-
else:
239-
target_units = {unit.device_class: unit}
240-
for device_class, target_unit in target_units.items():
241-
display_precision = ROUNDING_RULES.get(target_unit, 0)
242-
options = { "unit_of_measurement": target_unit.symbol,
243-
"display_precision": display_precision,
244-
"suggested_display_precision": display_precision }
245-
for unique_id in self.hub.unit_sensor_unique_ids.get(unit_type, {}).get(device_class, []):
246-
entity_id = registry.async_get_entity_id(Platform.SENSOR, DOMAIN, unique_id)
247-
_LOGGER.debug("Setting %s to %s with display precision %s", entity_id, unit.symbol, display_precision)
248-
registry.async_update_entity_options(entity_id, Platform.SENSOR, options)
249219

250220
if not (update_info or update_setting):
251221
_LOGGER.debug("No account settings were changed.")
252-
reason = "account_update_nochanges" + ("_update_sensors" if update_all_units else "")
253-
return self.async_abort(reason=reason)
222+
return self.async_abort(reason="account_update_nochanges")
254223

255224
no_error = await self.api.member_update_info(update_info, update_setting)
256225
await self.hub.async_refresh(force_member=True)
@@ -283,12 +252,12 @@ def _show_account_settings_form(self, user_input: dict[str, Any]) -> ConfigFlowR
283252
vol.Required(
284253
str(API.GENDER),
285254
default=user_input.get(
286-
API.GENDER, getattr(self.member, API.GENDER, Gender.NONE).lower
255+
API.GENDER, getattr(self.member, API.GENDER, str(Gender.NONE))
287256
),
288257
): selector(
289258
{
290259
"select": {
291-
"options": [g.lower for g in Gender],
260+
"options": [g.name.lower() for g in Gender],
292261
"mode": "dropdown",
293262
"translation_key": "member_gender",
294263
}
@@ -311,32 +280,33 @@ def _get_measurement_schema(self, user_input: dict[str, Any]) -> vol.Schema:
311280
vol.Required(
312281
str(API.FEED_UNIT),
313282
default=user_input.get(
314-
API.FEED_UNIT, getattr(self.member, API.FEED_UNIT, DEFAULT_FEED).lower
283+
API.FEED_UNIT, getattr(self.member, API.FEED_UNIT, DEFAULT_FEED.name)
315284
),
316-
): self._unit_selector((Unit.CUPS, Unit.OUNCES, Unit.GRAMS, Unit.MILLILITERS)),
285+
): self._unit_selector(
286+
(UnitTypes.CUPS, UnitTypes.OUNCES, UnitTypes.GRAMS, UnitTypes.MILLILITERS)
287+
),
317288
vol.Required(
318289
str(API.WATER_UNIT),
319290
default=user_input.get(
320-
API.WATER_UNIT, getattr(self.member, API.WATER_UNIT, DEFAULT_WATER).lower
291+
API.WATER_UNIT, getattr(self.member, API.WATER_UNIT, DEFAULT_WATER.name)
321292
),
322-
): self._unit_selector((Unit.WATER_OUNCES, Unit.WATER_MILLILITERS)),
293+
): self._unit_selector((UnitTypes.OUNCES, UnitTypes.MILLILITERS)),
323294
vol.Required(
324295
str(API.WEIGHT_UNIT),
325296
default=user_input.get(
326297
API.WEIGHT_UNIT,
327-
getattr(self.member, API.WEIGHT_UNIT, DEFAULT_WEIGHT).lower,
298+
getattr(self.member, API.WEIGHT_UNIT, DEFAULT_WEIGHT.name),
328299
),
329-
): self._unit_selector((Unit.POUNDS, Unit.KILOGRAMS)),
330-
vol.Optional("update_all_units", default=user_input.get("update_all_units", False)): bool,
300+
): self._unit_selector((UnitTypes.POUNDS, UnitTypes.KILOGRAMS)),
331301
}
332302
)
333303

334-
def _unit_selector(self, options: tuple[Unit, ...]) -> Any:
304+
def _unit_selector(self, options: tuple[Enum, ...]) -> Any:
335305
"""Return a dropdown selector for measurement unit options."""
336306
return selector(
337307
{
338308
"select": {
339-
"options": [o.lower for o in options],
309+
"options": [o.name.lower() for o in options],
340310
"mode": "dropdown",
341311
"translation_key": "unit_type",
342312
}
@@ -354,7 +324,7 @@ def validate_enum(self, api_key: str, form_value: Any, enum_cls: type[Enum]) ->
354324

355325
form_value_str = str(form_value).upper()
356326
if form_value_str in enum_cls.__members__:
357-
return enum_cls[form_value_str]
327+
return enum_cls[form_value_str].value
358328

359329
_LOGGER.error("Invalid value: %s for API key: %s", form_value, api_key)
360330
return None
@@ -388,7 +358,6 @@ def collect_updates(
388358
else:
389359
api_value = form_value
390360

391-
if api_value != current_value:
392-
updates[api_key] = api_value
393-
361+
updates[api_key] = api_value
362+
394363
return updates

custom_components/petlibro/const.py

Lines changed: 45 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, UnitOfMass, UnitOfVolume
66

7-
type _Unit = Unit
8-
97
DOMAIN = "petlibro"
108

119
# Configuration keys
@@ -21,6 +19,42 @@
2119
UPDATE_INTERVAL_SECONDS = 60 # You can adjust this value based on your needs
2220

2321

22+
class UnitTypes(IntEnum):
23+
"""Weight, feed, and water units with symbols."""
24+
25+
CUPS = 1, "cup"
26+
OUNCES = 2, UnitOfMass.OUNCES
27+
GRAMS = 3, UnitOfMass.GRAMS
28+
MILLILITERS = 4, UnitOfVolume.MILLILITERS
29+
KILOGRAMS = 5, UnitOfMass.KILOGRAMS
30+
POUNDS = 6, UnitOfMass.POUNDS
31+
32+
def __new__(cls, value: int, symbol: str):
33+
"Ensures IntEnum functionality while allowing symbols."
34+
obj = int.__new__(cls, value)
35+
obj._value_ = value
36+
obj._symbol = symbol # noqa: SLF001
37+
return obj
38+
39+
def __str__(self) -> str:
40+
"""Returns the name as a string."""
41+
return self.name
42+
43+
def __json__(self) -> int:
44+
"""Returns the int value when sending to the API."""
45+
return int(self)
46+
47+
@property
48+
def symbol(self) -> str:
49+
"""Returns unit symbol."""
50+
return self._symbol
51+
52+
53+
DEFAULT_WEIGHT = UnitTypes.POUNDS
54+
DEFAULT_FEED = UnitTypes.CUPS
55+
DEFAULT_WATER = UnitTypes.OUNCES
56+
57+
2458
class Gender(IntEnum):
2559
"""Gender/sex options."""
2660

@@ -38,10 +72,13 @@ def __new__(cls, value: int, icon: str, symbol: str, emoji: str):
3872
obj._emoji = emoji # noqa: SLF001
3973
return obj
4074

41-
@property
42-
def lower(self) -> str:
43-
"""Returns unit name in lower case."""
44-
return self.name.lower()
75+
def __str__(self) -> str:
76+
"""Returns the name as a string."""
77+
return self.name
78+
79+
def __json__(self) -> int:
80+
"""Returns the int value when sending to API."""
81+
return int(self)
4582

4683
@property
4784
def icon(self) -> str:
@@ -59,120 +96,13 @@ def emoji(self) -> str:
5996
return self._emoji
6097

6198

62-
class APIKey(StrEnum):
99+
class CommonAPIKeys(StrEnum):
63100
"""Common API JSON keys."""
64101

65-
# Common
66-
ID = "id"
67-
NAME = "name"
68-
WEIGHT = "weight"
69-
70-
# Member
102+
ACCOUNT_ID = "id"
71103
EMAIL = "email"
72104
NICKNAME = "nickname"
73105
GENDER = "gender"
74106
FEED_UNIT = "feedUnitType"
75107
WATER_UNIT = "waterUnitType"
76108
WEIGHT_UNIT = "weightUnitType"
77-
78-
# Pet
79-
BIRTHDAY = "birthday"
80-
TYPE = "type"
81-
SEX = "gender"
82-
BREED_NAME = "breedName"
83-
BREED_ID = "breedId"
84-
PET_ID = "petId"
85-
86-
87-
class Unit(IntEnum):
88-
"""Weight, feed, and water units with symbols and conversion."""
89-
90-
CUPS = 1, 1/12, "cup", ""
91-
OUNCES = 2, 0.35, UnitOfMass.OUNCES, "weight"
92-
GRAMS = 3, 10, UnitOfMass.GRAMS, "weight"
93-
MILLILITERS = 4, 20, UnitOfVolume.MILLILITERS, "volume"
94-
95-
KILOGRAMS = 5, 1, UnitOfMass.KILOGRAMS, "weight"
96-
POUNDS = 6, 2.20459, UnitOfMass.POUNDS, "weight"
97-
98-
WATER_OUNCES = 2 +6, 0.035195, UnitOfVolume.FLUID_OUNCES, "volume"
99-
WATER_MILLILITERS = 4 +6, 1, UnitOfVolume.MILLILITERS, "volume"
100-
101-
# KILOGRAMS, POUNDS, and WATER_ values can be converted using HA's built-in unit
102-
# converter, so their "factor"s and "device_class"s likely won't be used much or at all.
103-
104-
# WATER_ int values must be different to avoid aliasing. Take care when using .value
105-
106-
def __new__(cls, value: int, factor: float, symbol: str, device_class: str):
107-
"Ensures IntEnum functionality while allowing extra attributes."
108-
109-
obj = int.__new__(cls, value if value <= 6 else value - 6)
110-
obj._value_ = value
111-
obj._factor = factor # noqa: SLF001
112-
obj._symbol = symbol # noqa: SLF001
113-
obj._device_class = device_class # noqa: SLF001
114-
return obj
115-
116-
@property
117-
def lower(self) -> str:
118-
"""Returns unit name in lower case."""
119-
return self.name.lower()
120-
121-
@property
122-
def factor(self) -> float:
123-
"""Returns unit conversion factor."""
124-
return self._factor
125-
126-
@property
127-
def symbol(self) -> str:
128-
"""Returns unit symbol."""
129-
return self._symbol
130-
131-
@property
132-
def device_class(self) -> str:
133-
"""Returns unit device class."""
134-
return self._device_class
135-
136-
@classmethod
137-
def round(self, value: float, unit: _Unit):
138-
return round(value, ROUNDING_RULES.get(unit, 0))
139-
140-
@classmethod
141-
def convert_feed(
142-
self, value: float, from_unit: _Unit | None, to_unit: _Unit | None, rounded: bool = False
143-
):
144-
"""Convert PetLibro feed units. Use **None** for portion unit (1/12th of a cup)."""
145-
if value and from_unit != to_unit:
146-
if not {from_unit, to_unit}.issubset(VALID_UNIT_TYPES[APIKey.FEED_UNIT]):
147-
raise ValueError(f"Incompatible conversion: {from_unit} -> {to_unit}")
148-
149-
from_factor = from_unit.factor if from_unit else 1
150-
to_factor = to_unit.factor if to_unit else 1
151-
152-
api_value = value / from_factor
153-
new_value = api_value * to_factor
154-
else:
155-
new_value = value
156-
157-
if not to_unit:
158-
return round(new_value)
159-
if rounded:
160-
return Unit.round(new_value, to_unit)
161-
return new_value
162-
163-
164-
DEFAULT_WEIGHT = Unit.POUNDS
165-
DEFAULT_FEED = Unit.CUPS
166-
DEFAULT_WATER = Unit.WATER_OUNCES
167-
MAX_FEED_PORTIONS = 48
168-
VALID_UNIT_TYPES: dict[str, set[Unit]] = {
169-
APIKey.WEIGHT_UNIT: {Unit.POUNDS, Unit.KILOGRAMS, None},
170-
APIKey.FEED_UNIT: {Unit.CUPS, Unit.OUNCES, Unit.GRAMS, Unit.MILLILITERS, None},
171-
APIKey.WATER_UNIT: {Unit.WATER_OUNCES, Unit.WATER_MILLILITERS, None},
172-
}
173-
ROUNDING_RULES = {
174-
Unit.CUPS: 3, Unit.OUNCES: 2, Unit.POUNDS: 2, Unit.WATER_OUNCES: 2, Unit.KILOGRAMS: 2
175-
}
176-
WATER_MAPPING = {
177-
Unit.MILLILITERS: Unit.WATER_MILLILITERS, Unit.OUNCES: Unit.WATER_OUNCES
178-
}

custom_components/petlibro/devices/device.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,16 @@
55

66
from ..api import PetLibroAPI
77
from .event import Event, EVENT_UPDATE
8-
from ..member import Member
98

109

1110
_LOGGER = getLogger(__name__)
1211

1312

1413
class Device(Event):
15-
def __init__(self, data: dict, member: Member, api: PetLibroAPI):
14+
def __init__(self, data: dict, api: PetLibroAPI):
1615
super().__init__()
1716
self._data: dict = {}
1817
self.api = api
19-
self.member = member
2018

2119
self.update_data(data)
2220

0 commit comments

Comments
 (0)