Skip to content

Commit

Permalink
Add method for fetching item persistence data (#30) (#32)
Browse files Browse the repository at this point in the history
* add method for fetching item persistence data (#30)

* add type hint
  • Loading branch information
sim0nx authored Oct 23, 2023
1 parent 16df761 commit d607932
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 10 deletions.
21 changes: 20 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ work in progress.
- python :: dateutil
- python :: httpx
- python :: authlib
- openHAB version 3
- openHAB version 3 / 4

# Installation

Expand All @@ -29,6 +29,9 @@ Example usage of the library:

```python

import datetime
import json

from openhab import OpenHAB

base_url = 'http://localhost:8080/rest'
Expand Down Expand Up @@ -69,6 +72,22 @@ lights_group.on()
# send update to each member
for v in lights_group.members.values():
v.update('OFF')

# start_time for fetching persistence data
start_time = datetime.datetime.fromtimestamp(1695504300123 / 1000, tz=datetime.UTC)

# fetch persistence data using the OpenHAB client object
for k in openhab.get_item_persistence(knx_day_night.name,
page_length=20,
start_time=start_time
):
print(json.dumps(k, indent=4))

# fetch persistence data using the item directly
for k in item.persistence(page_length=20,
start_time=start_time
):
print(json.dumps(k, indent=4))
```

# Note on NULL and UNDEF
Expand Down
60 changes: 56 additions & 4 deletions openhab/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# along with python-openhab. If not, see <http://www.gnu.org/licenses/>.
#

# pylint: disable=bad-indentation
import datetime
import logging
import typing

Expand Down Expand Up @@ -133,7 +133,7 @@ def _check_req_return(req: httpx.Response) -> None:
if not 200 <= req.status_code < 300:
req.raise_for_status()

def req_get(self, uri_path: str) -> typing.Any:
def req_get(self, uri_path: str, params: typing.Optional[typing.Union[typing.Dict[str, typing.Any], list, tuple]] = None) -> typing.Any:
"""Helper method for initiating a HTTP GET request.
Besides doing the actual request, it also checks the return value and returns the resulting decoded
Expand All @@ -145,7 +145,7 @@ def req_get(self, uri_path: str) -> typing.Any:
Returns:
dict: Returns a dict containing the data returned by the OpenHAB REST server.
"""
r = self.session.get(self.url_rest + uri_path)
r = self.session.get(f'{self.url_rest}{uri_path}', params=params)
self._check_req_return(r)
return r.json()

Expand Down Expand Up @@ -395,7 +395,7 @@ def create_or_update_item(self,

if function_name is not None:
if function_name not in (
'EQUALITY', 'AND', 'OR', 'NAND', 'NOR', 'AVG', 'SUM', 'MAX', 'MIN', 'COUNT', 'LATEST', 'EARLIEST'):
'EQUALITY', 'AND', 'OR', 'NAND', 'NOR', 'AVG', 'SUM', 'MAX', 'MIN', 'COUNT', 'LATEST', 'EARLIEST'):
raise ValueError(f'Invalid function name "{function_name}')

if function_name in ('AND', 'OR', 'NAND', 'NOR') and (not function_params or len(function_params) != 2):
Expand All @@ -412,3 +412,55 @@ def create_or_update_item(self,
self.logger.debug('About to create item with PUT request:\n%s', str(paramdict))

self.req_put(f'/items/{name}', json_data=paramdict, headers={'Content-Type': 'application/json'})

def get_item_persistence(self,
name: str,
service_id: typing.Optional[str] = None,
start_time: typing.Optional[datetime.datetime] = None,
end_time: typing.Optional[datetime.datetime] = None,
page: int = 0,
page_length: int = 0,
boundary: bool = False,
) -> typing.Iterator[typing.Dict[str, typing.Union[str, int]]]:
"""Method for fetching persistence data for a given item.
Args:
name: The item name persistence data should be fetched for.
service_id: ID of the persistence service. If not provided the default service will be used.
start_time: Start time of the data to return. Will default to 1 day before end_time.
end_time: End time of the data to return. Will default to current time.
page: Page number of data to return. Defaults to 0 if not provided.
page_length: The length of each page. Defaults to 0 which disabled paging.
boundary: Gets one value before and after the requested period.
Returns:
Iterator over dict values containing time and state value, e.g.
{"time": 1695588900122,
"state": "23"
}
"""
params: typing.Dict[str, typing.Any] = {'boundary': str(boundary).lower(),
'page': page,
'pagelength': page_length,
}

if service_id is not None:
params['serviceId'] = service_id

if start_time is not None:
params['starttime'] = start_time.isoformat()

if end_time is not None:
params['endtime'] = end_time.isoformat()

if start_time == end_time:
raise ValueError('start_time must differ from end_time')

res = self.req_get(f'/persistence/items/{name}', params=params)

yield from res['data']

while page_length > 0 and int(res['datapoints']) > 0:
params['page'] += 1
res = self.req_get(f'/persistence/items/{name}', params=params)
yield from res['data']
40 changes: 36 additions & 4 deletions openhab/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
# along with python-openhab. If not, see <http://www.gnu.org/licenses/>.
#

# pylint: disable=bad-indentation

import datetime
import logging
Expand Down Expand Up @@ -206,7 +205,7 @@ def __str__(self) -> str:
"""String representation."""
state = self._state
if self._unitOfMeasure and not isinstance(self._state, tuple):
state = f'{self._state} {self._unitOfMeasure}'
state = f'{self._state} {self._unitOfMeasure}'
return f'<{self.type_} - {self.name} : {state}>'

def _update(self, value: typing.Any) -> None:
Expand Down Expand Up @@ -283,6 +282,39 @@ def is_state_undef(self) -> bool:

return False

def persistence(self,
service_id: typing.Optional[str] = None,
start_time: typing.Optional[datetime.datetime] = None,
end_time: typing.Optional[datetime.datetime] = None,
page: int = 0,
page_length: int = 0,
boundary: bool = False,
) -> typing.Iterator[typing.Dict[str, typing.Union[str, int]]]:
"""Method for fetching persistence data for a given item.
Args:
service_id: ID of the persistence service. If not provided the default service will be used.
start_time: Start time of the data to return. Will default to 1 day before end_time.
end_time: End time of the data to return. Will default to current time.
page: Page number of data to return. Defaults to 0 if not provided.
page_length: The length of each page. Defaults to 0 which disabled paging.
boundary: Gets one value before and after the requested period.
Returns:
Iterator over dict values containing time and state value, e.g.
{"time": 1695588900122,
"state": "23"
}
"""
yield from self.openhab.get_item_persistence(name=self.name,
service_id=service_id,
start_time=start_time,
end_time=end_time,
page=page,
page_length=page_length,
boundary=boundary,
)


class GroupItem(Item):
"""String item type."""
Expand Down Expand Up @@ -476,9 +508,9 @@ def _rest_format(self, value: typing.Union[float, typing.Tuple[float, str], str]
str or bytes: A string or bytes as converted from the value parameter.
"""
if isinstance(value, tuple) and len(value) == 2:
return super()._rest_format(f'{value[0]:G} {value[1]}')
return super()._rest_format(f'{value[0]:G} {value[1]}')
if not isinstance(value, str):
return super()._rest_format(f'{value:G}')
return super()._rest_format(f'{value:G}')
return super()._rest_format(value)


Expand Down
19 changes: 18 additions & 1 deletion test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#
# Georges Toth (c) 2016-present <[email protected]>
Expand All @@ -20,6 +19,8 @@


import datetime
import json

import openhab

base_url = 'http://localhost:8080/rest'
Expand All @@ -41,3 +42,19 @@
knx_day_night.off()

print(knx_day_night.state)

# start_time for fetching persistence data
start_time = datetime.datetime.fromtimestamp(1695504300123 / 1000, tz=datetime.UTC)

# fetch persistence data using the OpenHAB client object
for k in openhab.get_item_persistence(knx_day_night.name,
page_length=20,
start_time=start_time
):
print(json.dumps(k, indent=4))

# fetch persistence data using the item directly
for k in knx_day_night.persistence(page_length=20,
start_time=start_time
):
print(json.dumps(k, indent=4))

0 comments on commit d607932

Please sign in to comment.