Skip to content

Commit

Permalink
make event_manager async
Browse files Browse the repository at this point in the history
  • Loading branch information
hahn-th committed Jul 6, 2024
1 parent 07ad647 commit b691b2b
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/homematicip/events/event_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def unsubscribe(self, event_type, callback):
if callback in self._subscriptions[event_type]:
self._subscriptions[event_type].remove(callback)

async def publish(self, event_type: ModelUpdateEvent, event_args):
async def async_publish(self, event_type: ModelUpdateEvent, event_args):
if event_type in self._subscriptions:
for callback in self._subscriptions[event_type]:
await callback(event_type, event_args)
20 changes: 10 additions & 10 deletions src/homematicip/events/hmip_event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,71 +32,71 @@ async def process_event_async(self, event_json):
data = event['client']
client: Client = Client(**data)
self._model.clients[data['id']] = client
await self._event_manager.publish(ModelUpdateEvent.ITEM_CREATED, client)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_CREATED, client)

elif event_type == EventType.CLIENT_CHANGED:
data = event['client']
if data['id'] in self._model.clients:
client: HmipBaseModel = self._model.clients[data['id']]
client.update_from_dict(data)
await self._event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, client)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, client)

elif event_type == EventType.CLIENT_REMOVED:
data = event['client']

client = self._model.clients.pop(data['id'], None)
if client is not None:
client.remove_object()
await self._event_manager.publish(ModelUpdateEvent.ITEM_REMOVED, client)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_REMOVED, client)

elif event_type == EventType.DEVICE_ADDED:
data = event['device']
if data['id'] not in self._model.devices:
device: Device = Device(**data)
self._model.devices[data['id']] = device
await self._event_manager.publish(ModelUpdateEvent.ITEM_CREATED, device)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_CREATED, device)

elif event_type == EventType.DEVICE_CHANGED:
data = event['device']
if data['id'] in self._model.devices:
device: HmipBaseModel = self._model.devices[data['id']]
device.update_from_dict(data)
await self._event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, device)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, device)

elif event_type == EventType.DEVICE_REMOVED:
data = event['device']

device = self._model.devices.pop(data['id'], None)
if device is not None:
device.remove_object()
await self._event_manager.publish(ModelUpdateEvent.ITEM_REMOVED, device)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_REMOVED, device)

elif event_type == EventType.GROUP_ADDED:
data = event['group']
if data['id'] not in self._model.groups:
group: Group = Group(**data)
self._model.groups[data['id']] = group
await self._event_manager.publish(ModelUpdateEvent.ITEM_CREATED, group)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_CREATED, group)

elif event_type == EventType.GROUP_CHANGED:
data = event['group']
if data['id'] in self._model.groups:
group: HmipBaseModel = self._model.groups[data['id']]
group.update_from_dict(data)
await self._event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, group)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, group)
elif event_type == EventType.GROUP_REMOVED:
data = event['group']

group = self._model.groups.pop(data['id'], None)
if group is not None:
group.remove_object()
await self._event_manager.publish(ModelUpdateEvent.ITEM_REMOVED, group)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_REMOVED, group)

elif event_type == EventType.HOME_CHANGED:
data = event['home']

self._model.home.update_from_dict(data)
await self._event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, self._model.home)
await self._event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, self._model.home)

elif event_type == EventType.SECURITY_JOURNAL_CHANGED:
pass
2 changes: 2 additions & 0 deletions src/homematicip/model/hmip_base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

try:
from pydantic.v1 import BaseModel, PrivateAttr # type: ignore # noqa F401 # pragma: no cover
except ImportError:
Expand Down
16 changes: 8 additions & 8 deletions src/homematicip/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def build_model_from_json(data) -> Model:
LOGGER.fatal("Error while building model from json.", exc_info=e)


def update_model_from_json(model: Model, event_manager: EventManager, data):
async def async_update_model_from_json(model: Model, event_manager: EventManager, data):
"""Update a model from json data.
:param model: The model to update.
Expand All @@ -48,37 +48,37 @@ def update_model_from_json(model: Model, event_manager: EventManager, data):
for key in data:
if key == "home":
if model.home.update_from_dict(data[key]):
event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, model.home)
await event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, model.home)
elif key == "clients":
for client_json in data[key].items():
client_id = client_json[0]
client_data = client_json[1]
if client_id in model.clients:
if model.clients[client_id].update_from_dict(client_data):
event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, model.clients[client_id])
await event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, model.clients[client_id])
else:
model.clients[client_id] = Client(**client_data)
event_manager.publish(ModelUpdateEvent.ITEM_CREATED, model.clients[client_id])
await event_manager.async_publish(ModelUpdateEvent.ITEM_CREATED, model.clients[client_id])
elif key == "devices":
for device_json in data[key].items():
device_id = device_json[0]
device_data = device_json[1]
if device_id in model.devices:
if model.devices[device_id].update_from_dict(device_data):
event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, model.devices[device_id])
await event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, model.devices[device_id])
else:
model.devices[device_id] = Device(**device_data)
event_manager.publish(ModelUpdateEvent.ITEM_CREATED, model.devices[device_id])
await event_manager.async_publish(ModelUpdateEvent.ITEM_CREATED, model.devices[device_id])
elif key == "groups":
for group_json in data[key].items():
group_id = group_json[0]
group_data = group_json[1]
if group_id in model.groups:
if model.groups[group_id].update_from_dict(group_data):
event_manager.publish(ModelUpdateEvent.ITEM_UPDATED, model.groups[group_id])
await event_manager.async_publish(ModelUpdateEvent.ITEM_UPDATED, model.groups[group_id])
else:
model.groups[group_id] = Group(**group_data)
event_manager.publish(ModelUpdateEvent.ITEM_CREATED, model.groups[group_id])
await event_manager.async_publish(ModelUpdateEvent.ITEM_CREATED, model.groups[group_id])

except ValidationError as e:
LOGGER.fatal("Error while updating model from json.", exc_info=e)
8 changes: 4 additions & 4 deletions src/homematicip/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from homematicip.events.event_manager import EventManager
from homematicip.events.event_types import ModelUpdateEvent
from homematicip.events.hmip_event_handler import HmipEventHandler
from homematicip.model.model import Model, build_model_from_json, update_model_from_json
from homematicip.model.model import Model, build_model_from_json, async_update_model_from_json

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -135,7 +135,7 @@ async def async_get_current_state(self):
async def async_refresh_model(self):
"""Refresh the model with the current state from the access point."""
current_configuration = await self.async_get_current_state()
update_model_from_json(self.model, self.event_manager, current_configuration)
await async_update_model_from_json(self.model, self.event_manager, current_configuration)

async def _async_start_listening_for_updates(self, context: ConnectionContext):
self.websocket_handler = WebSocketHandler()
Expand Down Expand Up @@ -172,9 +172,9 @@ def _publish_websocket_connected(self, connected_value: bool):
:param connected_value: The new state of the websocket connection."""
if connected_value:
self.event_manager.publish(ModelUpdateEvent.HOME_CONNECTED, connected_value)
self.event_manager.async_publish(ModelUpdateEvent.HOME_CONNECTED, connected_value)
else:
self.event_manager.publish(ModelUpdateEvent.HOME_DISCONNECTED, connected_value)
self.event_manager.async_publish(ModelUpdateEvent.HOME_DISCONNECTED, connected_value)

@property
def rest_connection(self):
Expand Down
46 changes: 23 additions & 23 deletions tests/model/test_model.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import copy
import json
from unittest.mock import Mock
from unittest.mock import Mock, AsyncMock

from homematicip.model.model import build_model_from_json, update_model_from_json
from homematicip.model.model import build_model_from_json, async_update_model_from_json
from homematicip.model.model_components import GroupChannelReference, Group, Device
from homematicip.model.hmip_base import HmipBaseModel
from homematicip.model.home import Home, FunctionalHome
Expand Down Expand Up @@ -130,7 +130,7 @@ def test_hmip_base_model_subscribers_after_update(sample_data_complete):
assert base.devices[device_id].lastStatusUpdate == device.lastStatusUpdate


def test_update_home_from_json(sample_data_complete):
async def test_update_home_from_json(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
Expand All @@ -139,23 +139,23 @@ def test_update_home_from_json(sample_data_complete):

model_current = build_model_from_json(sample_data_complete)
model_expected = build_model_from_json(manipulated)
event_manager = Mock()
event_manager = AsyncMock()

old_state_climate = model_current.home.functionalHomes['INDOOR_CLIMATE'].active
new_state_climate = model_expected.home.functionalHomes['INDOOR_CLIMATE'].active
old_state_connected = model_current.home.connected
new_state_connected = model_expected.home.connected

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert len(event_manager.mock_calls) == 1
assert old_state_climate != new_state_climate
assert old_state_connected != new_state_connected


def test_update_model_from_json_changed_devices(sample_data_complete):
async def test_update_model_from_json_changed_devices(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
Expand All @@ -164,119 +164,119 @@ def test_update_model_from_json_changed_devices(sample_data_complete):

model_current = build_model_from_json(sample_data_complete)
model_expected = build_model_from_json(manipulated)
event_manager = Mock()
event_manager = AsyncMock()

old_state_duty = model_current.devices['3014F7110000RAIN_SENSOR'].functionalChannels["0"].dutyCycle
new_state_duty = model_expected.devices['3014F7110000RAIN_SENSOR'].functionalChannels["0"].dutyCycle
old_state_on = model_current.devices['3014F7110DIN_RAIL_SWITCH'].functionalChannels["1"].on
new_state_on = model_expected.devices['3014F7110DIN_RAIL_SWITCH'].functionalChannels["1"].on

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert len(event_manager.mock_calls) == 2
assert old_state_duty != new_state_duty
assert old_state_on != new_state_on


def test_update_model_from_json_with_new_device(sample_data_complete):
async def test_update_model_from_json_with_new_device(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
manipulated["devices"]["3014F7110000RAIN_SENSO2"] = copy.deepcopy(
sample_data_complete["devices"]["3014F7110000RAIN_SENSOR"])

model_current = build_model_from_json(sample_data_complete)
event_manager = Mock()
event_manager = AsyncMock()
contains_device_before = "3014F7110000RAIN_SENSO2" in model_current.devices

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert contains_device_before is False
assert "3014F7110000RAIN_SENSO2" in model_current.devices
assert len(event_manager.mock_calls) == 1


def test_update_model_from_json_updated_group(sample_data_complete):
async def test_update_model_from_json_updated_group(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
manipulated["groups"]["00000000-0000-0000-0000-0000000000EN"]["label"] = "EnergyGroupHWR2"

model_current = build_model_from_json(sample_data_complete)
model_expected = build_model_from_json(manipulated)
event_manager = Mock()
event_manager = AsyncMock()

old_label = model_current.groups['00000000-0000-0000-0000-0000000000EN'].label
new_label = model_expected.groups['00000000-0000-0000-0000-0000000000EN'].label

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert len(event_manager.mock_calls) == 1
assert old_label != new_label
assert model_current.groups['00000000-0000-0000-0000-0000000000EN'].label == new_label


def test_update_model_from_json_with_new_group(sample_data_complete):
async def test_update_model_from_json_with_new_group(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
manipulated["groups"]["00000000-0000-0000-0000-0000000000EN2"] = copy.deepcopy(
sample_data_complete["groups"]["00000000-0000-0000-0000-0000000000EN"])

model_current = build_model_from_json(sample_data_complete)
event_manager = Mock()
event_manager = AsyncMock()
contains_group_before = "00000000-0000-0000-0000-0000000000EN2" in model_current.groups

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert contains_group_before is False
assert "00000000-0000-0000-0000-0000000000EN2" in model_current.groups
assert len(event_manager.mock_calls) == 1


def test_update_model_from_json_with_new_client(sample_data_complete):
async def test_update_model_from_json_with_new_client(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
manipulated["clients"]["00000000-0000-0000-0000-000000000NEW"] = copy.deepcopy(
sample_data_complete["clients"]["00000000-0000-0000-0000-000000000000"])

model_current = build_model_from_json(sample_data_complete)
event_manager = Mock()
event_manager = AsyncMock()
contains_client_before = "00000000-0000-0000-0000-000000000NEW" in model_current.clients

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert contains_client_before is False
assert "00000000-0000-0000-0000-000000000NEW" in model_current.clients
assert len(event_manager.mock_calls) == 1


def test_update_model_from_json_updated_client(sample_data_complete):
async def test_update_model_from_json_updated_client(sample_data_complete):
"""Test, if the home is updated correctly."""
# arrange
manipulated = copy.deepcopy(sample_data_complete)
manipulated["clients"]["00000000-0000-0000-0000-000000000000"]["label"] = "ChangedLabel"

model_current = build_model_from_json(sample_data_complete)
model_expected = build_model_from_json(manipulated)
event_manager = Mock()
event_manager = AsyncMock()

old_label = model_current.clients['00000000-0000-0000-0000-000000000000'].label
new_label = model_expected.clients['00000000-0000-0000-0000-000000000000'].label

# act
update_model_from_json(model_current, event_manager, manipulated)
await async_update_model_from_json(model_current, event_manager, manipulated)

# assert
assert len(event_manager.mock_calls) == 1
Expand Down
6 changes: 3 additions & 3 deletions tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_runner_websocket_connected_event_is_raised(mocker):
"""Test that the HOME_CONNECTED event is raised when the websocket connection is established."""
mock_publish = mocker.Mock()
runner = Runner()
runner.event_manager.publish = mock_publish
runner.event_manager.async_publish = mock_publish
runner._websocket_connected = False

runner._set_websocket_connected_state(True)
Expand All @@ -76,7 +76,7 @@ def test_runner_websocket_disconnected_event_is_raised(mocker):
"""Test that the HOME_DISCONNECTED event is raised when the websocket connection is lost."""
mock_publish = mocker.Mock()
runner = Runner()
runner.event_manager.publish = mock_publish
runner.event_manager.async_publish = mock_publish
runner._websocket_connected = True

runner._set_websocket_connected_state(False)
Expand All @@ -102,7 +102,7 @@ async def test_runner_refresh_model(mocker, filled_model, sample_data_complete):
mocked_get_current_state = mocker.patch("homematicip.runner.Runner.async_get_current_state", new_callable=AsyncMock)
mocked_get_current_state.return_value = sample_data_complete

mocked_update_function = mocker.patch("homematicip.runner.update_model_from_json")
mocked_update_function = mocker.patch("homematicip.runner.async_update_model_from_json")

# act
await runner.async_refresh_model()
Expand Down

0 comments on commit b691b2b

Please sign in to comment.