Skip to content

Commit

Permalink
Merge_try (#350)
Browse files Browse the repository at this point in the history
* added more logs

* Log updated

* Logger updated

* Remove log

* Add connection counter for debugging

* Hearbeat modified

* Start heartbeat after connecting

* Update get_state.py

* Update heartbeat.py

* Update pyvlx.py

* Updated log

* update log

* update log

* fix stop on orientation command

* Revert "Update heartbeat.py"

This reverts commit f9f0251.

* Remove pass exception

* adding logs

* version update

* version changed

* Showing correct branch in the logs

* Logs correcting

* Heartbeat fixed

* Version updated

* Reducing traffic

* HSM short disabling after reconnection

* Version update

* Heartbeat start on connect

* Rebase

* fix imports

* fix imports

* alphabetic sorting

* some thread safety and hearbeat restart if already running to ensure it has not failed

* Update setup.py

* Update pyvlx.py

* Rebuilt heartbeat in an (IMHO) cleaner way. Old heartbeat prevented hass from shutting down for upt o 60s

* Await heartbeat stop

* Update Version

* Add OnOffSwitch to pyvlx

* Add bytes representation of SwitchParameter

* Set switch states from state nofitications

* removed import version

* Update version

* Change states only after reaching target

* Add opening/closing info

* Add debug for opening/closing info

* Add support for DualRollerShutter

* Removed orientation reference in DualRollerShutter

* Add active_paramer value for DualRollerShutter

* Just send on FP for DualRollerShutter

* Set unused Parameter to Target in DualRollerShutt

* Add DuealRollerShutterPosition

* Fix for out_of_range for DualRoller

* Add DUAL parameter into str representation

* Updated comments

* Fix upper/lower curtain position

* Fix wrong position on DualRollerShutter

* Updated unittests

* Update DualRollerShutter only with valid value

* Increase version

* Remove debug messages

* Update "is opening/closing" logic

* Use state info for opening/closing

* Add OperatingState to frames

* adjust version in debug printout

* Update const

* Version update

* Allow for the velocity to be set when opening/closing/setting position of opening devices

* Allow for the velocity to be set when opening/closing/setting position of opening devices: extra boolean for HA configuration entity

* Update setup.py

* Update pyvlx.py

* version update

* Support various undefined operating states

* Add velocity for DualRollerShutter

* poll states during heartbeat

* Update setup.py

* Update pyvlx.py

* Fix #33

* adjustable open close positions

* add spec

* catch lost commands

* update version

* add debug message

* Imrpove commands

* remain session_id

* Clear event

* fix await

* Use Semaphore

* Increase parallel updates

* update log

* update version

* Update pyvlx.py

* Update setup.py

* add missing import

* Fix linter issues

* Fix linter

* Fix Linter

* Fix flake8

* fix flake

* several fixes

* fix isort issues

* update command_send.py

* fix flake8

* fix flake8

* fix pylint

* fix Flake8

* fix isort

* fix linting

* correct version

* fix paramter.py

* Add more specific exceptions

* fix OperatingState in frames

* ignore type

* version information

* Revert serial_number assertion

* version update for testing

* fix opening_device.py

* version update for testing

* remove version

* Add docstrings

* fix linter

* fix linting

* fix isort

* remove unnecessary else

* fix serial_number arguments

* update version

* Update semaphore

* Clear asyncio event

* Cleanup command_send.oy

* Revert to return early approach

* Add tests

* add docstring

* Add test

* fix isort

* Add docstring

* Remove unused imports

* Add typing

* Add mock for PyVLX

* Remove unused imports

* Add type ignore hints

* add type hints

* Add type hints

* Add further tests

* Fix target_position

* update version for testing

* correct version

* Update ci.yml

* Update ci.yml

* Add type hints

* Adding tests

* Add mocked_loop

* Add type hints

* Update ci.yml

* Update ci.yml

* Update typing and testing

* add typing

* add typing

* Reduce logs

* remove log import

* fix version

* Remove pyvlx object from log entry

---------

Co-authored-by: Christopher Hoch <chris@hpc.at>
Co-authored-by: Christopher Hoch <chris.hoch@hpc.at>
  • Loading branch information
3 people authored Dec 12, 2023
1 parent c9736bf commit 46992b4
Showing 48 changed files with 1,296 additions and 619 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -99,7 +99,10 @@ jobs:
- name: Create coverage report
run: |
coverage combine coverage*/.coverage*
coverage report --fail-under=81
coverage report --fail-under=80
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.3.2
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
4 changes: 4 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -11,3 +11,7 @@ output-format=colorized
[FORMAT]
indent-string=' '
max-line-length=150

[MAIN]
max-branches=20
max-statements=55
2 changes: 2 additions & 0 deletions pyvlx/__init__.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@
from .klf200gateway import Klf200Gateway
from .lightening_device import Light, LighteningDevice
from .log import PYVLXLOG
from .node import Node
from .nodes import Nodes
from .on_off_switch import OnOffSwitch
from .opening_device import (
Awning, Blade, Blind, GarageDoor, Gate, OpeningDevice, RollerShutter,
Window)
3 changes: 2 additions & 1 deletion pyvlx/api/__init__.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
# flake8: noqa

from .activate_scene import ActivateScene
from .api_event import ApiEvent
from .command_send import CommandSend
from .factory_default import FactoryDefault
from .get_all_nodes_information import GetAllNodesInformation
@@ -14,7 +15,7 @@
from .get_state import GetState
from .get_version import GetVersion
from .house_status_monitor import (
house_status_monitor_disable, house_status_monitor_enable)
HouseStatusMonitorDisable, HouseStatusMonitorEnable)
from .leave_learn_state import LeaveLearnState
from .password_enter import PasswordEnter
from .reboot import Reboot
1 change: 1 addition & 0 deletions pyvlx/api/api_event.py
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ async def do_api_call(self) -> None:
await self.send_frame()
await self.start_timeout()
await self.response_received_or_timeout.wait()
self.response_received_or_timeout.clear()
await self.stop_timeout()
self.pyvlx.connection.unregister_frame_received_cb(self.response_rec_callback)

9 changes: 8 additions & 1 deletion pyvlx/api/command_send.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Module for retrieving scene list from API."""
from typing import TYPE_CHECKING, Any, Optional

from ..exception import PyVLXException
from ..parameter import Parameter
from .api_event import ApiEvent
from .frames import (
@@ -24,7 +25,7 @@ def __init__(
parameter: Parameter,
active_parameter: int = 0,
wait_for_completion: bool = True,
timeout_in_seconds: int = 60,
timeout_in_seconds: int = 2,
**functional_parameter: Any
):
"""Initialize SceneList class."""
@@ -66,6 +67,12 @@ async def handle_frame(self, frame: FrameBase) -> bool:
return True
return False

async def send(self) -> None:
"""Send frame to KLF200."""
await self.do_api_call()
if not self.success:
raise PyVLXException("Unable to send command")

def request_frame(self) -> FrameCommandSendRequest:
"""Construct initiating frame."""
self.session_id = get_new_session_id()
4 changes: 2 additions & 2 deletions pyvlx/api/frames/frame_command_send.py
Original file line number Diff line number Diff line change
@@ -106,9 +106,9 @@ def __str__(self) -> str:
Position(Parameter(bytes(value))),
)
return (
'<{} node_ids="{}" parameter="{}" functional_parameter="{}" '
'<{} node_ids="{}" active_parameter="{}" parameter="{}" functional_parameter="{}" '
'session_id="{}" originator="{}"/>'.format(
type(self).__name__, self.node_ids,
type(self).__name__, self.node_ids, self.active_parameter,
self.parameter, functional_parameter,
self.session_id, self.originator,
)
17 changes: 11 additions & 6 deletions pyvlx/api/frames/frame_get_all_nodes_information.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@
from enum import Enum
from typing import Optional

from pyvlx.const import Command, NodeTypeWithSubtype, NodeVariation, Velocity
from pyvlx.const import (
Command, NodeTypeWithSubtype, NodeVariation, OperatingState, Velocity)
from pyvlx.exception import PyVLXException
from pyvlx.parameter import Parameter
from pyvlx.string_helper import bytes_to_string, string_to_bytes
@@ -36,7 +37,11 @@ class FrameGetAllNodesInformationConfirmation(FrameBase):

PAYLOAD_LEN = 2

def __init__(self, status: AllNodesInformationStatus = AllNodesInformationStatus.OK, number_of_nodes: int = 0):
def __init__(
self,
status: AllNodesInformationStatus = AllNodesInformationStatus.OK,
number_of_nodes: int = 0,
):
"""Init Frame."""
super().__init__(Command.GW_GET_ALL_NODES_INFORMATION_CFM)
self.status = status
@@ -78,7 +83,7 @@ def __init__(self) -> None:
self.power_mode = 0
self.build_number = 0
self._serial_number = bytes(8)
self.state = 0
self.state = OperatingState.UNKNOWN
self.current_position = Parameter()
self.target = Parameter()
self.current_position_fp1 = Parameter()
@@ -123,7 +128,7 @@ def get_payload(self) -> bytes:
payload += bytes([self.power_mode])
payload += bytes([self.build_number])
payload += bytes(self._serial_number)
payload += bytes([self.state])
payload += bytes([self.state.value])
payload += bytes(self.current_position.raw)
payload += bytes(self.target.raw)
payload += bytes(self.current_position_fp1.raw)
@@ -150,7 +155,7 @@ def from_payload(self, payload: bytes) -> None:
self.power_mode = payload[74]
self.build_number = payload[75]
self._serial_number = payload[76:84]
self.state = payload[84]
self.state = OperatingState(payload[84])
self.current_position = Parameter(payload[85:87])
self.target = Parameter(payload[87:89])
self.current_position_fp1 = Parameter(payload[89:91])
@@ -189,7 +194,7 @@ def __str__(self) -> str:
self.power_mode,
self.build_number,
self.serial_number,
self.state,
self.state.name,
self.current_position,
self.target,
self.current_position_fp1,
11 changes: 6 additions & 5 deletions pyvlx/api/frames/frame_get_node_information.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@
from enum import Enum
from typing import Optional

from pyvlx.const import Command, NodeTypeWithSubtype, NodeVariation, Velocity
from pyvlx.const import (
Command, NodeTypeWithSubtype, NodeVariation, OperatingState, Velocity)
from pyvlx.exception import PyVLXException
from pyvlx.parameter import Parameter
from pyvlx.string_helper import bytes_to_string, string_to_bytes
@@ -94,7 +95,7 @@ def __init__(self) -> None:
self.power_mode = 0
self.build_number = 0
self._serial_number = bytes(8)
self.state = 0
self.state = OperatingState.UNKNOWN
self.current_position = Parameter()
self.target = Parameter()
self.current_position_fp1 = Parameter()
@@ -141,7 +142,7 @@ def get_payload(self) -> bytes:
[self.build_number]
) # <-- hey @VELUX: your documentation is wrong here
payload += bytes(self._serial_number)
payload += bytes([self.state])
payload += bytes([self.state.value])
payload += bytes(self.current_position.raw)
payload += bytes(self.target.raw)
payload += bytes(self.current_position_fp1.raw)
@@ -169,7 +170,7 @@ def from_payload(self, payload: bytes) -> None:
75
] # <-- hey @VELUX: your documentation is wrong here
self._serial_number = payload[76:84]
self.state = payload[84]
self.state = OperatingState(payload[84])
self.current_position = Parameter(payload[85:87])
self.target = Parameter(payload[87:89])
self.current_position_fp1 = Parameter(payload[89:91])
@@ -208,7 +209,7 @@ def __str__(self) -> str:
self.power_mode,
self.build_number,
self.serial_number,
self.state,
self.state.name,
self.current_position,
self.target,
self.current_position_fp1,
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
import struct
from datetime import datetime

from pyvlx.const import Command
from pyvlx.const import Command, OperatingState
from pyvlx.parameter import Parameter

from .frame import FrameBase
@@ -17,7 +17,7 @@ def __init__(self) -> None:
"""Init Frame."""
super().__init__(Command.GW_NODE_STATE_POSITION_CHANGED_NTF)
self.node_id = 0
self.state = 0
self.state: OperatingState = OperatingState.NON_EXECUTING
self.current_position = Parameter()
self.target = Parameter()
self.current_position_fp1 = Parameter()
@@ -30,7 +30,7 @@ def __init__(self) -> None:
def get_payload(self) -> bytes:
"""Return Payload."""
payload = bytes([self.node_id])
payload += bytes([self.state])
payload += bytes([self.state.value])
payload += bytes(self.current_position.raw)
payload += bytes(self.target.raw)
payload += bytes(self.current_position_fp1.raw)
@@ -44,7 +44,7 @@ def get_payload(self) -> bytes:
def from_payload(self, payload: bytes) -> None:
"""Init frame from binary data."""
self.node_id = payload[0]
self.state = payload[1]
self.state = OperatingState(payload[1])
self.current_position = Parameter(payload[2:4])
self.target = Parameter(payload[4:6])
self.current_position_fp1 = Parameter(payload[6:8])
@@ -71,7 +71,7 @@ def __str__(self) -> str:
'remaining_time="{}" time="{}"/>'.format(
type(self).__name__,
self.node_id,
self.state,
self.state.name,
self.current_position,
self.target,
self.current_position_fp1,
18 changes: 0 additions & 18 deletions pyvlx/api/house_status_monitor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""Module for house status monitor."""
from typing import TYPE_CHECKING

from pyvlx.exception import PyVLXException

from .api_event import ApiEvent
from .frames import (
FrameBase, FrameHouseStatusMonitorDisableConfirmation,
@@ -52,19 +50,3 @@ async def handle_frame(self, frame: FrameBase) -> bool:
def request_frame(self) -> FrameHouseStatusMonitorDisableRequest:
"""Construct initiating frame."""
return FrameHouseStatusMonitorDisableRequest()


async def house_status_monitor_enable(pyvlx: "PyVLX") -> None:
"""Enable house status monitor."""
status_monitor_enable = HouseStatusMonitorEnable(pyvlx=pyvlx)
await status_monitor_enable.do_api_call()
if not status_monitor_enable.success:
raise PyVLXException("Unable enable house status monitor.")


async def house_status_monitor_disable(pyvlx: "PyVLX") -> None:
"""Disable house status monitor."""
status_monitor_disable = HouseStatusMonitorDisable(pyvlx=pyvlx)
await status_monitor_disable.do_api_call()
if not status_monitor_disable.success:
raise PyVLXException("Unable disable house status monitor.")
25 changes: 19 additions & 6 deletions pyvlx/connection.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Module for handling the TCP connection with Gateway."""
import asyncio
import ssl
import sys
from typing import Callable, Coroutine, List, Optional

from .api.frame_creation import frame_from_raw
@@ -37,7 +38,11 @@ def get_next_token(self) -> Optional[bytes]:
class TCPTransport(asyncio.Protocol):
"""Class for handling asyncio connection transport."""

def __init__(self, frame_received_cb: Callable[[FrameBase], None], connection_closed_cb: Callable[[], None]):
def __init__(
self,
frame_received_cb: Callable[[FrameBase], None],
connection_closed_cb: Callable[[], None],
):
"""Init TCPTransport."""
self.frame_received_cb = frame_received_cb
self.connection_closed_cb = connection_closed_cb
@@ -52,9 +57,13 @@ def data_received(self, data: bytes) -> None:
while self.tokenizer.has_tokens():
raw = self.tokenizer.get_next_token()
assert raw is not None
frame = frame_from_raw(raw)
if frame is not None:
self.frame_received_cb(frame)

try:
frame = frame_from_raw(raw)
if frame is not None:
self.frame_received_cb(frame)
except PyVLXException:
PYVLXLOG.error("Error in data_received", exc_info=sys.exc_info())

def connection_lost(self, exc: object) -> None:
"""Handle lost connection."""
@@ -85,6 +94,7 @@ def disconnect(self) -> None:
if self.transport is not None:
self.transport.close()
self.transport = None
self.connected = False

async def connect(self) -> None:
"""Connect to gateway via SSL."""
@@ -98,6 +108,9 @@ async def connect(self) -> None:
)
self.connected = True
self.connection_counter += 1
PYVLXLOG.debug(
"Amount of connections since last HA start: %s", self.connection_counter
)

def register_frame_received_cb(self, callback: CallbackType) -> None:
"""Register frame received callback."""
@@ -110,7 +123,7 @@ def unregister_frame_received_cb(self, callback: CallbackType) -> None:
def write(self, frame: FrameBase) -> None:
"""Write frame to Bus."""
if not isinstance(frame, FrameBase):
raise PyVLXException("Frame not of type FrameBase", frame_type=type(frame))
raise PyVLXException("Frame not of type FrameBase", *type(frame))
PYVLXLOG.debug("SEND: %s", frame)
assert self.transport is not None
self.transport.write(slip_pack(bytes(frame)))
@@ -132,4 +145,4 @@ def frame_received_cb(self, frame: FrameBase) -> None:

def connection_closed_cb(self) -> None:
"""Server closed connection."""
self.connected = False
self.disconnect()
Loading

0 comments on commit 46992b4

Please sign in to comment.