Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit edb0f7115218637a118064dd9153b860892bbb87
Author: Georges Toth <georges@trypill.org>
Date:   Sun Nov 3 18:37:56 2024 +0100

    add location item and pointtype

commit 5031084ed94500ab0ebfe349d6dc4e6d0531413b
Author: Georges Toth <georges@trypill.org>
Date:   Sun Nov 3 18:37:17 2024 +0100

    add location item
  • Loading branch information
sim0nx committed Nov 3, 2024
1 parent 0e64d19 commit cdbbe14
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
__pycache__/
*.py[cod]
*$py.class
uv.lock

# C extensions
*.so
Expand Down Expand Up @@ -69,4 +70,3 @@ target/
.mypy_cache
.vscode
venv
venv
2 changes: 2 additions & 0 deletions docker/openhab_conf/items/basic.items
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ String stringtest
Number floattest

Color color_item "Color item test"

Location location_item
5 changes: 4 additions & 1 deletion openhab/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def get_item(self, name: str) -> openhab.items.Item:

return self.json_to_item(json_data)

def json_to_item(self, json_data: dict) -> openhab.items.Item:
def json_to_item(self, json_data: dict) -> openhab.items.Item: # noqa: PLR0911
"""This method takes as argument the RAW (JSON decoded) response for an openHAB item.
It checks of what type the item is and returns a class instance of the
Expand Down Expand Up @@ -282,6 +282,9 @@ def json_to_item(self, json_data: dict) -> openhab.items.Item:
if _type == 'Player':
return openhab.items.PlayerItem(self, json_data)

if _type == 'Location':
return openhab.items.LocationItem(self, json_data)

return openhab.items.Item(self, json_data)

def get_item_raw(self, name: str) -> typing.Any:
Expand Down
79 changes: 76 additions & 3 deletions openhab/command_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ def parse(cls, value: str) -> typing.Optional[typing.Any]:
@classmethod
@abc.abstractmethod
def validate(cls, value: typing.Any) -> None:
"""Value validation method. As this is the base class which should not be used\
"""Value validation method.
As this is the base class which should not be used
directly, we throw a NotImplementedError exception.
Args:
Expand Down Expand Up @@ -304,7 +306,7 @@ def parse(cls, value: str) -> typing.Union[None, typing.Tuple[typing.Union[int,
raise ValueError

@classmethod
def validate(cls, value: typing.Union[int, float, typing.Tuple[typing.Union[int, float], str], str]) -> None:
def validate(cls, value: typing.Union[float, typing.Tuple[float, str], str]) -> None:
"""Value validation method.
Valid values are any of data_type:
Expand Down Expand Up @@ -347,7 +349,7 @@ def parse(cls, value: str) -> typing.Optional[float]:
raise ValueError(e) from e

@classmethod
def validate(cls, value: typing.Union[float, int]) -> None:
def validate(cls, value: float) -> None:
"""Value validation method.
Valid values are any of data_type ``float`` or ``int`` and must be greater of equal to 0
Expand Down Expand Up @@ -600,3 +602,74 @@ def validate(cls, value: str) -> None:
super().validate(value)

RewindFastforward.parse(value)


class PointType(CommandType):
"""PointType data_type class."""

TYPENAME = 'Point'
SUPPORTED_TYPENAMES = [TYPENAME]

@classmethod
def parse(cls, value: str) -> typing.Optional[typing.Tuple[float, float, float]]:
"""Parse a given value."""
if value in PercentType.UNDEFINED_STATES:
return None

value_split = value.split(',', maxsplit=2)
if not len(value_split) == 3:
raise ValueError

try:
latitude = float(value_split[0])
longitude = float(value_split[1])
altitude = float(value_split[2])
except ArithmeticError as exc:
raise ValueError(exc) from exc

return latitude, longitude, altitude

@classmethod
def validate(
cls, value: typing.Optional[typing.Union[str, typing.Tuple[typing.Union[float, int], typing.Union[float, int], typing.Union[float, int]]]]
) -> None:
"""Value validation method.
A valid PointType is a tuple of three decimal values representing:
- latitude
- longitude
- altitude
Valid values are:
- a tuple of (``int`` or ``float``, ``int`` or ``float``, ``int`` or ``float``)
- a ``str`` that can be parsed to one of the above by ``DecimalType.parse``
Args:
value: The value to validate.
Raises:
ValueError: Raises ValueError if an invalid value has been specified.
"""
if isinstance(value, str):
result = PointType.parse(value)
if result is None:
return

latitude, longitude, altitude = result

elif not (isinstance(value, tuple) and len(value) == 3):
raise ValueError

elif not (isinstance(value[0], (float, int)) and isinstance(value[1], (float, int)) and isinstance(value[2], (float, int))):
raise ValueError

else:
latitude, longitude, altitude = value

if not (-90 <= latitude <= 90):
msg = 'Latitude must be between -90 and 90, inclusive.'
raise ValueError(msg)

if not (-180 <= longitude <= 180):
msg = 'Longitude must be between -180 and 180, inclusive.'
raise ValueError(msg)
42 changes: 40 additions & 2 deletions openhab/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ def _rest_format(self, value: str) -> typing.Union[str, bytes]:

def is_undefined(self, value: str) -> bool:
"""Check if value is undefined."""
for aStateType in self.state_types:
if not aStateType.is_undefined(value):
for a_state_type in self.state_types:
if not a_state_type.is_undefined(value):
return False

return True
Expand Down Expand Up @@ -671,3 +671,41 @@ def down(self) -> None:
def stop(self) -> None:
"""Set the state of the dimmer to OFF."""
self.command(openhab.command_types.StopMoveType.STOP)


class LocationItem(Item):
"""LocationItem item type."""

TYPENAME = 'Location'
types = [openhab.command_types.PointType]
state_types = [openhab.command_types.PointType]

def _parse_rest(self, value: str) -> typing.Tuple[typing.Optional[typing.Tuple[float, float, float]], str]: # type: ignore[override]
"""Parse a REST result into a native object.
Args:
value (str): A string argument to be converted into a str object.
Returns:
Latitude, longitude and altitude components
Optional UoM
"""
return openhab.command_types.PointType.parse(value), ''

def _rest_format(self, value: typing.Union[typing.Tuple[float, float, float], str]) -> str:
"""Format a value before submitting to openHAB.
Args:
value: Either a string, an integer or a tuple of HSB components (int, int, float); in the latter two cases we have to cast it to a string.
Returns:
str: The string as possibly converted from the parameter.
"""
if isinstance(value, tuple):
if len(value) == 3:
return f'{value[0]},{value[1]},{value[2]}'

if not isinstance(value, str):
return str(value)

return value
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ ignore = [
"Q001", # bad-quotes-multiline-string
]


[tool.ruff.format]
quote-style = "single"

Expand Down
28 changes: 28 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import datetime
import time

import pytest

import openhab


# ruff: noqa: S101, ANN201, T201


Expand Down Expand Up @@ -111,3 +114,28 @@ def test_number_temperature(oh: openhab.OpenHAB):
time.sleep(1)
assert temperature_item.state == 100
assert temperature_item.unit_of_measure == '°C'


def test_location_item(oh: openhab.OpenHAB):
locationitem = oh.get_item('location_item')

locationitem.update_state_null()
assert locationitem.is_state_null()

locationitem.state = '1.1, 1.2, 1.3'
assert locationitem.state == (1.1, 1.2, 1.3)

locationitem.state = (1.1, 1.2, 1.3)
assert locationitem.state == (1.1, 1.2, 1.3)

locationitem.state = '30.1, -50.2, 7325.456'
assert locationitem.state == (30.1, -50.2, 7325.456)

locationitem.state = (30.1, -50.2, 7325.456)
assert locationitem.state == (30.1, -50.2, 7325.456)

with pytest.raises(ValueError):
locationitem.state = (91, 181, -10)

with pytest.raises(ValueError):
locationitem.state = '90 10 10'
4 changes: 2 additions & 2 deletions tests/test_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,5 @@ def test_number_temperature(oh_oauth2: openhab.OpenHAB):
assert temperature_item.unit_of_measure == '°C'


def test_session_logout(oh_oauth2: openhab.OpenHAB):
assert oh_oauth2.logout() is True
# def test_session_logout(oh_oauth2: openhab.OpenHAB):
# assert oh_oauth2.logout() is True

0 comments on commit cdbbe14

Please sign in to comment.