Skip to content

Commit e6d8b4f

Browse files
authored
fix series 3 behavior sensor bug (#22)
1 parent 38197a7 commit e6d8b4f

4 files changed

Lines changed: 126 additions & 16 deletions

File tree

custom_components/tryfi/pytryfi/fiPet.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def updateAllDetails(self, session: requests.Session):
191191
try:
192192
self.updateBehaviorStats(session)
193193
except Exception as e:
194-
LOGGER.debug(f"Could not update behavior stats for Pet {self.name}. This may be an older collar model.\n{e}")
194+
LOGGER.warning(f"Could not update behavior stats for Pet {self.name}.\n{e}")
195195
# Behavior stats may not be available for older collars
196196
pass
197197

@@ -200,8 +200,20 @@ def updateBehaviorStats(self, sessionId: requests.Session):
200200
"""Update behavior statistics for Series 3+ collars."""
201201
healthTrendsJSON = query.getPetHealthTrends(sessionId, self.petId, 'DAY')
202202
behavior_trends = healthTrendsJSON.get('behaviorTrends', [])
203+
203204
self.setBehaviorStatsFromTrends(behavior_trends)
204205

206+
def _parseBehaviorDuration(self, input: str) -> int:
207+
# examples: '46min'
208+
if input.startswith('<'):
209+
return 0
210+
elif input.endswith('min'):
211+
return int(input.replace('min', ''), 10)
212+
elif input.endswith('hr'):
213+
return int(input.replace('hr', ''), 10) * 60
214+
else:
215+
return int(input)
216+
205217
def setBehaviorStatsFromTrends(self, behaviorTrends):
206218
"""Parse behavior data from health trends API."""
207219
# Reset all metrics
@@ -238,16 +250,7 @@ def setBehaviorStatsFromTrends(self, behaviorTrends):
238250
duration_summary = summary.get('durationSummary')
239251
duration_seconds = 0
240252
if duration_summary:
241-
if duration_summary.startswith('<'):
242-
duration_seconds = 30
243-
else:
244-
parts = duration_summary.replace('h', '').replace('m', '').split()
245-
if len(parts) == 2:
246-
duration_seconds = int(parts[0]) * 3600 + int(parts[1]) * 60
247-
elif 'h' in duration_summary:
248-
duration_seconds = int(parts[0]) * 3600
249-
else:
250-
duration_seconds = int(parts[0]) * 60
253+
duration_seconds = self._parseBehaviorDuration(duration_summary)
251254

252255
# Map to our attributes
253256
if trend_id == 'barking:DAY':

custom_components/tryfi/sensor.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ def __init__(self, coordinator: Any, pet: FiPet, behavior_type: str, metric_type
593593
self._attr_native_unit_of_measurement = "events"
594594
self._attr_state_class = SensorStateClass.MEASUREMENT
595595
else: # duration
596-
self._attr_native_unit_of_measurement = UnitOfTime.SECONDS
596+
self._attr_native_unit_of_measurement = UnitOfTime.MINUTES
597597
self._attr_device_class = SensorDeviceClass.DURATION
598598
self._attr_state_class = SensorStateClass.MEASUREMENT
599599

@@ -605,6 +605,11 @@ def __init__(self, coordinator: Any, pet: FiPet, behavior_type: str, metric_type
605605
model="Series 3+ Collar",
606606
)
607607

608+
def _fipet_attr_name(self) -> str:
609+
attr_name = f"{self._period}{self._behavior_type.title()}{'Count' if self._metric_type == 'count' else 'Duration'}"
610+
611+
return attr_name
612+
608613
@property
609614
def native_value(self) -> StateType:
610615
"""Return the behavior metric value."""
@@ -613,12 +618,11 @@ def native_value(self) -> StateType:
613618
return None
614619

615620
# Build attribute name
616-
attr_name = f"{self._period}{self._behavior_type.title()}{'Count' if self._metric_type == 'count' else 'Duration'}"
617-
attr_name = attr_name.replace("Licking", "Licking").replace("Barking", "Barking").replace("Scratching", "Scratching").replace("Eating", "Eating").replace("Drinking", "Drinking")
621+
attr_name = self._fipet_attr_name()
618622

619623
# Get the value from the pet object
620-
value = getattr(pet, attr_name, 0)
621-
624+
value = getattr(pet, attr_name, None)
625+
622626
# Return 0 if None
623627
return value if value is not None else 0
624628

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[
2+
{
3+
"__typename": "PetHealthTrend",
4+
"id": "barking:DAY",
5+
"title": "Barking",
6+
"summaryComponents": {
7+
"__typename": "PetHealthTrendSummary",
8+
"eventsSummary": "24 events",
9+
"durationSummary": "46min"
10+
}
11+
},
12+
{
13+
"__typename": "PetHealthTrend",
14+
"id": "eating:DAY",
15+
"title": "Eating",
16+
"summaryComponents": {
17+
"__typename": "PetHealthTrendSummary",
18+
"eventsSummary": "6 events",
19+
"durationSummary": "6min"
20+
}
21+
},
22+
{
23+
"__typename": "PetHealthTrend",
24+
"id": "drinking:DAY",
25+
"title": "Drinking",
26+
"summaryComponents": {
27+
"__typename": "PetHealthTrendSummary",
28+
"eventsSummary": "3 events",
29+
"durationSummary": "<1min"
30+
}
31+
},
32+
{
33+
"__typename": "PetHealthTrend",
34+
"id": "cleaning_self:DAY",
35+
"title": "Licking",
36+
"summaryComponents": {
37+
"__typename": "PetHealthTrendSummary",
38+
"eventsSummary": "6 events",
39+
"durationSummary": "6min"
40+
}
41+
},
42+
{
43+
"__typename": "PetHealthTrend",
44+
"id": "scratching:DAY",
45+
"title": "Scratching",
46+
"summaryComponents": {
47+
"__typename": "PetHealthTrendSummary",
48+
"eventsSummary": "4 events",
49+
"durationSummary": "1min"
50+
}
51+
}
52+
]

tests/pytryfi/test_pet.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from custom_components.tryfi.pytryfi import FiPet, FiDevice
22
from .utils import mock_graphql, GRAPHQL_FIXTURE_PET_ALL_INFO, REQ_PET_ALL_INFO
33

4+
import json
45
import requests
56
import responses
7+
import urllib.parse
68

79

810
@responses.activate
@@ -31,3 +33,52 @@ def test_get_sleep():
3133

3234
assert pet.dailySleep == 60
3335
assert pet.dailyNap == 30
36+
37+
38+
@responses.activate
39+
def test_update_behavior_stats():
40+
with open("tests/pytryfi/fixture_petHealthTrends.json", "r") as f:
41+
health_trends_fixture = json.load(f)
42+
43+
behavior_trends_response = {
44+
"getPetHealthTrendsForPet": {"behaviorTrends": health_trends_fixture}
45+
}
46+
47+
qString = """
48+
query PetHealthTrends {
49+
getPetHealthTrendsForPet(petId: "test-pet", period: DAY) {
50+
behaviorTrends {
51+
__typename
52+
id
53+
title
54+
summaryComponents {
55+
__typename
56+
eventsSummary
57+
durationSummary
58+
}
59+
}
60+
}
61+
}
62+
"""
63+
url = f"https://api.tryfi.com/graphql?query={urllib.parse.quote_plus(qString)}"
64+
responses.add(
65+
method=responses.GET,
66+
url=url,
67+
status=200,
68+
json={"data": behavior_trends_response},
69+
)
70+
71+
pet = FiPet("test-pet")
72+
pet._device = FiDevice("device-id")
73+
pet.updateBehaviorStats(requests.Session())
74+
75+
assert pet.dailyBarkingCount == 24
76+
assert pet.dailyBarkingDuration == 46
77+
assert pet.dailyEatingCount == 6
78+
assert pet.dailyEatingDuration == 6
79+
assert pet.dailyDrinkingCount == 3
80+
assert pet.dailyDrinkingDuration == 0
81+
assert pet.dailyLickingCount == 6
82+
assert pet.dailyLickingDuration == 6
83+
assert pet.dailyScratchingCount == 4
84+
assert pet.dailyScratchingDuration == 1

0 commit comments

Comments
 (0)