Skip to content

Commit

Permalink
1.1.2 (#396)
Browse files Browse the repository at this point in the history
  • Loading branch information
spacemanspiff2007 authored Jun 19, 2023
1 parent 311ed71 commit ab962ef
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 31 deletions.
6 changes: 5 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ MyOpenhabRule()
```

# Changelog
#### 1.1.2 (2023-06-19)
- Re-added `ItemStateEventFilter`
- Improved parsing of `DateTime` values

#### 1.1.1 (2023-06-16)
- Fixed a bug where the rule context was not found

Expand All @@ -136,7 +140,7 @@ This is a breaking change!
- Renamed `GroupItemStateChangedEvent` to `GroupStateChangedEvent`
- Groups issue a `GroupStateUpdateEvent` when the state updates on OH3 (consistent with OH4 behavior)
- Groups work now with `ValueUpdateEvent` and `ValueChangedEvent` as expected
- Renamed `ItemStateEvent` to `ItemStateUpdatedEvent`
- ~~Renamed `ItemStateEvent` to `ItemStateUpdatedEvent`~~
- Ignored ItemStateEvent on OH4
- Fewer warnings for long-running functions (execution of <FUNC_NAME> took too long)
- `Thing` status and status_detail are now an Enum
Expand Down
2 changes: 1 addition & 1 deletion src/HABApp/__version__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Version scheme:
# X.X[.X] or X.X[.X].DEV-X

__version__ = '1.1.1'
__version__ = '1.1.2'
3 changes: 2 additions & 1 deletion src/HABApp/openhab/events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
from .channel_events import ChannelTriggeredEvent, ChannelDescriptionChangedEvent
from .thing_events import ThingStatusInfoChangedEvent, ThingStatusInfoEvent, \
ThingFirmwareStatusInfoEvent, ThingAddedEvent, ThingRemovedEvent, ThingUpdatedEvent, ThingConfigStatusInfoEvent
from .event_filters import ItemStateUpdatedEventFilter, ItemStateChangedEventFilter, ItemCommandEventFilter
from .event_filters import ItemStateUpdatedEventFilter, ItemStateEventFilter, ItemStateChangedEventFilter, \
ItemCommandEventFilter
8 changes: 7 additions & 1 deletion src/HABApp/openhab/events/event_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

from HABApp.core.const import MISSING
from HABApp.core.events.filter.event import TypeBoundEventFilter
from . import ItemStateChangedEvent, ItemStateUpdatedEvent, ItemCommandEvent
from . import ItemStateChangedEvent, ItemStateEvent, ItemStateUpdatedEvent, ItemCommandEvent


# Todo: Drop this when we go OH4.0 only
class ItemStateEventFilter(TypeBoundEventFilter):
def __init__(self, value: Any = MISSING):
super().__init__(ItemStateEvent, value=value)


class ItemStateUpdatedEventFilter(TypeBoundEventFilter):
Expand Down
12 changes: 11 additions & 1 deletion src/HABApp/openhab/items/datetime_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from typing import TYPE_CHECKING, Optional, FrozenSet, Mapping

from HABApp.core.const.const import PYTHON_311
from HABApp.openhab.items.base_item import OpenhabItem, MetaData

if TYPE_CHECKING:
Expand All @@ -24,7 +25,16 @@ class DatetimeItem(OpenhabItem):

@staticmethod
def _state_from_oh_str(state: str):
dt = datetime.strptime(state, '%Y-%m-%dT%H:%M:%S.%f%z')
# see implementation im map_values.py
if PYTHON_311:
dt = datetime.fromisoformat(state)
else:
pos_dot = state.find('.')
pos_plus = state.find('+')
if pos_plus - pos_dot > 6:
state = state[:pos_dot + 7] + state[pos_plus:]
dt = datetime.strptime(state, '%Y-%m-%dT%H:%M:%S.%f%z')

# all datetime objs from openHAB have a timezone set so we can't easily compare them
# --> TypeError: can't compare offset-naive and offset-aware datetime
dt = dt.astimezone(tz=None) # Changes datetime object so it uses system timezone
Expand Down
25 changes: 15 additions & 10 deletions src/HABApp/openhab/map_values.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import datetime
from datetime import datetime

from HABApp.core.const.const import PYTHON_311
from HABApp.openhab.definitions import HSBValue, OnOffValue, OpenClosedValue, PercentValue, QuantityValue, RawValue, \
UpDownValue
from HABApp.openhab.definitions.values import PointValue


def map_openhab_values(openhab_type: str, openhab_value: str):
# because we preprocess the string value can be None.
# Either remove the preprocessing or remove this here
if openhab_value is None:
return None

assert isinstance(openhab_type, str), type(openhab_type)
assert isinstance(openhab_value, str), type(openhab_value)

Expand All @@ -29,17 +25,26 @@ def map_openhab_values(openhab_type: str, openhab_value: str):
if openhab_type == "String":
return openhab_value

if openhab_type == "HSB":
return HSBValue(openhab_value)

if openhab_type == "DateTime":
dt = datetime.datetime.strptime(openhab_value, '%Y-%m-%dT%H:%M:%S.%f%z')
# see implementation im datetime_item.py
if PYTHON_311:
dt = datetime.fromisoformat(openhab_value)
else:
pos_dot = openhab_value.find('.')
pos_plus = openhab_value.find('+')
if pos_plus - pos_dot > 6:
openhab_value = openhab_value[:pos_dot + 7] + openhab_value[pos_plus:]
dt = datetime.strptime(openhab_value, '%Y-%m-%dT%H:%M:%S.%f%z')

# all datetimes from openHAB have a timezone set, so we can't easily compare them
# --> TypeError: can't compare offset-naive and offset-aware datetimes
dt = dt.astimezone(tz=None) # Changes datetime object so it uses system timezone
dt = dt.replace(tzinfo=None) # Removes timezone awareness
return dt

if openhab_type == "HSB":
return HSBValue(openhab_value)

if openhab_type == 'OnOff':
return OnOffValue(openhab_value)

Expand Down
5 changes: 4 additions & 1 deletion tests/test_openhab/test_events/test_oh_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
def test_class_annotations():
"""EventFilter relies on the class annotations, so we test that every event has those"""

exclude = ('OpenhabEvent', 'ItemStateChangedEventFilter', 'ItemStateUpdatedEventFilter', 'ItemCommandEventFilter')
exclude = (
'OpenhabEvent',
'ItemStateChangedEventFilter', 'ItemStateUpdatedEventFilter', 'ItemStateEventFilter',
'ItemCommandEventFilter')
for cls in get_module_classes('HABApp.openhab.events', exclude).values():
check_class_annotations(
cls, init_alias={'initial_value': 'value', 'group_names': 'groups', 'thing_type': 'type'}
Expand Down
59 changes: 44 additions & 15 deletions tests/test_openhab/test_openhab_datatypes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from datetime import datetime

import pytest

from HABApp.openhab.items import DatetimeItem, NumberItem
from HABApp.openhab.map_values import map_openhab_values


Expand All @@ -8,25 +11,51 @@ def test_type_none():
assert map_openhab_values('Number', 'NULL') is None


def test_type_number():
assert 0 == map_openhab_values('Number', '0')
assert -99 == map_openhab_values('Number', '-99')
assert 99 == map_openhab_values('Number', '99')
@pytest.mark.parametrize('value, target', (('0', 0), ('-15', -15), ('55', 55), ))
def test_type_number(value: str, target: int):
ret = NumberItem._state_from_oh_str(value)
assert ret == target
assert isinstance(ret, int)

ret = map_openhab_values('Number', value)
assert ret == target
assert isinstance(ret, int)


@pytest.mark.parametrize(
'value, target', (('0.0', 0.0), ('-99.99', -99.99), ('99.99', 99.99), ('0', 0), ('-15', -15), ('55', 55), )
)
def test_type_decimal(value: str, target: int):
ret = NumberItem._state_from_oh_str(value)
assert ret == target
assert type(ret) is target.__class__

ret = map_openhab_values('Decimal', value)
assert ret == target
assert type(ret) is target.__class__


def __get_dt_parms():

# We have to build the offset str dynamically otherwise we will fail during CI because it's in another timezone
now = datetime.now()
offset_secs = int(now.astimezone().tzinfo.utcoffset(now).total_seconds())
hours = offset_secs // 3600
minutes = (offset_secs - 3600 * hours) // 60
assert offset_secs - hours * 3600 - minutes * 60 == 0
offset_str = f'{hours:02d}:{minutes:02d}'

def test_type_decimal():
assert 0.0 == map_openhab_values('Decimal', '0.0')
assert -99.99 == map_openhab_values('Decimal', '-99.99')
assert 99.99 == map_openhab_values('Decimal', '99.99')
assert 5 == map_openhab_values('Decimal', '5')
return (
pytest.param(f'2023-06-17T15:31:04.754673068+{offset_str}', datetime(2023, 6, 17, 15, 31, 4, 754673), id='T1'),
pytest.param(f'2023-06-17T15:31:04.754673+{offset_str}', datetime(2023, 6, 17, 15, 31, 4, 754673), id='T2'),
pytest.param(f'2023-06-17T15:31:04.754+{offset_str}', datetime(2023, 6, 17, 15, 31, 4, 754000), id='T3'),
)


def test_type_datetime():
# test now
_now = datetime.now().replace(microsecond=456000)
_in = _now.astimezone(None).strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + _now.astimezone(None).strftime('%z')
# 2019-10-09T07:37:00.000+0200
assert map_openhab_values('DateTime', _in) == _now
@pytest.mark.parametrize('value, target', __get_dt_parms())
def test_type_datetime(value: str, target: datetime):
assert DatetimeItem._state_from_oh_str(value) == target
assert map_openhab_values('DateTime', value) == target


def test_quantity():
Expand Down

0 comments on commit ab962ef

Please sign in to comment.