Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
default_language_version:
python: python3.12
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.2
hooks:
- id: ruff
args:
- --ignore=F401,E712
- --fix
- id: ruff-format
args:
- --line-length=120
8 changes: 7 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@
"-c",
"./pytest.ini"
],
}
"ruff.enable": true,
"ruff.fixAll": true,
"ruff.lint.enable": true,
"ruff.format.args": [
"--line-length=120"
]
}
8 changes: 2 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
"Operating System :: OS Independent",
"Development Status :: 2 - Pre-Alpha",
],
python_requires='>=3.7',
install_requires=[
"aiohttp",
"rx",
"pycryptodome"
],
python_requires=">=3.7",
install_requires=["aiohttp", "rx", "pycryptodome"],
)
11 changes: 3 additions & 8 deletions tests/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from mock import patch, Mock
import json
from xcomfort.bridge import Bridge
from xcomfort.devices import (Light, LightState)
from xcomfort.devices import Light, LightState
from xcomfort.constants import Messages


Expand All @@ -27,10 +27,7 @@ async def test_light_switch_on():
def test_lightstate_switch_on():
device = Light(None, 1, "", True)

payload = {
"switch": True,
"dimmvalue": 50
}
payload = {"switch": True, "dimmvalue": 50}

device.handle_state(payload)

Expand All @@ -41,9 +38,7 @@ def test_lightstate_switch_on():
def test_lightstate_switch_on_when_not_dimmable():
device = Light(None, 1, "", False)

payload = {
"switch": True
}
payload = {"switch": True}

device.handle_state(payload)

Expand Down
11 changes: 9 additions & 2 deletions tests/test_rctouch.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import pytest
import json
from xcomfort.devices import (RcTouch)
from xcomfort.devices import RcTouch


def test_rctouchstate():
payload = {"deviceId":17,"info":[{"text":"1222","type":2,"value":"20.9"},{"text":"1223","type":2,"icon":1,"value":"42.5"}]}
payload = {
"deviceId": 17,
"info": [
{"text": "1222", "type": 2, "value": "20.9"},
{"text": "1223", "type": 2, "icon": 1, "value": "42.5"},
],
}

device = RcTouch(None, 1, "", "1")

Expand Down
99 changes: 58 additions & 41 deletions xcomfort/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@
from enum import Enum
from .connection import SecureBridgeConnection, setup_secure_connection
from .constants import ComponentTypes, DeviceTypes, Messages
from .devices import (BridgeDevice, DoorSensor, Light, RcTouch, Heater, Rocker, Shade, WindowSensor)
from .devices import (
BridgeDevice,
DoorSensor,
Light,
RcTouch,
Heater,
Rocker,
Shade,
WindowSensor,
)

# Some HA code relies on bridge having imported these:
from .room import Room, RoomState, RctMode, RctState, RctModeRange # noqa
from .comp import Comp, CompState # noqa
from .room import Room, RoomState, RctMode, RctState, RctModeRange # noqa
from .comp import Comp, CompState # noqa


class State(Enum):
Uninitialized = 0
Initializing = 1
Ready = 2
Closing = 10


class Bridge:
def __init__(self, ip_address: str, authkey: str, session=None):
self.ip_address = ip_address
Expand All @@ -30,11 +42,13 @@ def __init__(self, ip_address: str, authkey: str, session=None):
self._closeSession = closeSession

# Values determined from using setpoint slider in app.
self.rctsetpointallowedvalues = dict({
RctMode.Cool: RctModeRange(5.0,20.0),
RctMode.Eco: RctModeRange(10.0,30.0),
RctMode.Comfort: RctModeRange(18.0,40.0)
})
self.rctsetpointallowedvalues = dict(
{
RctMode.Cool: RctModeRange(5.0, 20.0),
RctMode.Eco: RctModeRange(10.0, 30.0),
RctMode.Comfort: RctModeRange(18.0, 40.0),
}
)
self._comps = {}
self._devices = {}
self._rooms = {}
Expand All @@ -51,7 +65,7 @@ async def run(self):

while self.state != State.Closing:
try:
#self.logger(f"Connecting...")
# self.logger(f"Connecting...")
await self._connect()
await self.connection.pump()

Expand Down Expand Up @@ -88,42 +102,42 @@ def _add_room(self, room):

def _handle_SET_DEVICE_STATE(self, payload):
try:
device = self._devices[payload['deviceId']]
device = self._devices[payload["deviceId"]]

device.handle_state(payload)
except KeyError:
return

def _handle_SET_STATE_INFO(self, payload):
for item in payload['item']:
if 'deviceId' in item:
deviceId = item['deviceId']
for item in payload["item"]:
if "deviceId" in item:
deviceId = item["deviceId"]
device = self._devices[deviceId]
device.handle_state(item)

elif 'roomId' in item:
roomId = item['roomId']
elif "roomId" in item:
roomId = item["roomId"]
room = self._rooms[roomId]
room.handle_state(item)

elif 'compId' in item:
compId = item['compId']
elif "compId" in item:
compId = item["compId"]
comp = self._comps[compId]
comp.handle_state(item)

else:
self.logger(f"Unknown state info: {payload}")

def _create_comp_from_payload(self, payload):
comp_id = payload['compId']
name = payload['name']
comp_id = payload["compId"]
name = payload["name"]
comp_type = payload["compType"]

return Comp(self, comp_id, comp_type, name, payload)

def _create_device_from_payload(self, payload):
device_id = payload['deviceId']
name = payload['name']
device_id = payload["deviceId"]
name = payload["name"]
dev_type = payload["devType"]
comp_id = payload["compId"]
if dev_type in (DeviceTypes.ACTUATOR_SWITCH, DeviceTypes.ACTUATOR_DIMM):
Expand Down Expand Up @@ -157,13 +171,13 @@ def _create_device_from_payload(self, payload):
return BridgeDevice(self, device_id, name)

def _create_room_from_payload(self, payload):
room_id = payload['roomId']
name = payload['name']
room_id = payload["roomId"]
name = payload["name"]

return Room(self, room_id, name)

def _handle_comp_payload(self, payload):
comp_id = payload['compId']
comp_id = payload["compId"]

comp = self._comps.get(comp_id)

Expand All @@ -178,7 +192,7 @@ def _handle_comp_payload(self, payload):
comp.handle_state(payload)

def _handle_device_payload(self, payload):
device_id = payload['deviceId']
device_id = payload["deviceId"]

device = self._devices.get(device_id)

Expand All @@ -193,7 +207,7 @@ def _handle_device_payload(self, payload):
device.handle_state(payload)

def _handle_room_payload(self, payload):
room_id = payload['roomId']
room_id = payload["roomId"]

room = self._rooms.get(room_id)

Expand All @@ -208,31 +222,31 @@ def _handle_room_payload(self, payload):
room.handle_state(payload)

def _handle_SET_ALL_DATA(self, payload):
if 'lastItem' in payload:
if "lastItem" in payload:
self.state = State.Ready

if 'devices' in payload:
for device_payload in payload['devices']:
if "devices" in payload:
for device_payload in payload["devices"]:
try:
self._handle_device_payload(device_payload)
except Exception as e:
self.logger(f"Failed to handle device payload: {str(e)}")

if 'comps' in payload:
if "comps" in payload:
for comp_payload in payload["comps"]:
try:
self._handle_comp_payload(comp_payload)
except Exception as e:
self.logger(f"Failed to handle comp payload: {str(e)}")

if 'rooms' in payload:
if "rooms" in payload:
for room_payload in payload["rooms"]:
try:
self._handle_room_payload(room_payload)
except Exception as e:
self.logger(f"Failed to handle room payload: {str(e)}")

if 'roomHeating' in payload:
if "roomHeating" in payload:
for room_payload in payload["roomHeating"]:
try:
self._handle_room_payload(room_payload)
Expand All @@ -244,25 +258,28 @@ def _handle_UNKNOWN(self, message_type, payload):
pass

def _onMessage(self, message):

if 'payload' in message:
if "payload" in message:
# self.logger(f"Message: {message}")
message_type = Messages(message['type_int'])
method_name = '_handle_' + message_type.name
message_type = Messages(message["type_int"])
method_name = "_handle_" + message_type.name

method = getattr(self, method_name,
lambda p: self._handle_UNKNOWN(message_type, p))
method = getattr(
self, method_name, lambda p: self._handle_UNKNOWN(message_type, p)
)
try:
method(message['payload'])
method(message["payload"])
except Exception as e:
self.logger(f"Unknown error with: {method_name}: {str(e)}")
else:
self.logger(f"Not known: {message}")

async def _connect(self):
self.connection = await setup_secure_connection(self._session, self.ip_address, self.authkey)
self.connection = await setup_secure_connection(
self._session, self.ip_address, self.authkey
)
self.connection_subscription = self.connection.messages.subscribe(
self._onMessage)
self._onMessage
)

async def close(self):
self.state = State.Closing
Expand Down
2 changes: 2 additions & 0 deletions xcomfort/comp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import rx


class CompState:
def __init__(self, raw):
self.raw = raw
Expand All @@ -9,6 +10,7 @@ def __str__(self):

__repr__ = __str__


class Comp:
def __init__(self, bridge, comp_id, comp_type, name: str, payload: dict):
self.bridge = bridge
Expand Down
Loading