From 2b311eed7fba20977da355e036cddfab38d01ba3 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:15:35 -0500 Subject: [PATCH 01/31] Blinds example --- examples/blinds.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/examples/blinds.py b/examples/blinds.py index f439111..8b45df0 100644 --- a/examples/blinds.py +++ b/examples/blinds.py @@ -1,30 +1,42 @@ -from sinric import SinricPro, SinricProConstants + import asyncio +from collections.abc import Callable +from numbers import Real +from typing import Final, Union + +from sinric import SinricPro, SinricProConstants + +BlindsCallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], + Callable[[str, Real, str], tuple[bool, Real, str]] +] + +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +BLINDS_ID: Final[str] = '' -APP_KEY = '' -APP_SECRET = '' -BLINDS_ID = '' +# TODO: got this from _sinricpro_constants.py - POWER_STATE_ON is a str, could we consider changing it to a strEnum? -def set_power_state(device_id, state): +def set_power_state(device_id: str, state: str) -> tuple[bool, str]: print('device_id: {} state: {}'.format(device_id, state)) return True, state -def set_range_value(device_id, value, instance_id): +def set_range_value(device_id: str, value: Real, instance_id: str) -> tuple[bool, Real, str]: print('device_id: {} set to: {}'.format(device_id, value)) return True, value, instance_id -callbacks = { +callbacks: dict[str, BlindsCallbackFunctions] = { SinricProConstants.SET_POWER_STATE: set_power_state, SinricProConstants.SET_RANGE_VALUE: set_range_value } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [BLINDS_ID], callbacks, - enable_log=False, restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [BLINDS_ID], callbacks, + enable_log=False, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) # client.event_handler.raise_event(BLINDS_ID, SinricProConstants.SET_RANGE_VALUE, data = {SinricProConstants.RANGE_VALUE: 30 }) From 4208a69ab8d338acd84ced637e5d80b08bb4df2d Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:15:53 -0500 Subject: [PATCH 02/31] Added typings to _power_controller.py --- sinric/_power_controller.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sinric/_power_controller.py b/sinric/_power_controller.py index ebb9854..03bd09a 100644 --- a/sinric/_power_controller.py +++ b/sinric/_power_controller.py @@ -5,10 +5,13 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from collections.abc import Callable +from typing import Any from ._sinricpro_constants import SinricProConstants class PowerController: - async def power_state(self, jsn, power_state_callback): + # TODO: Maybe define a type for json? + async def power_state(self, jsn: Any, power_state_callback: Callable[[str, str], tuple[bool, str]]) -> tuple[bool, str]: return power_state_callback(jsn.get("payload").get(SinricProConstants.DEVICEID), jsn.get("payload").get("value").get("state")) From e9a0edeaab17fa985603106dbbb8d545c4a818d2 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:21:22 -0500 Subject: [PATCH 03/31] Added typings to _range_value_controller.py --- sinric/_range_value_controller.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sinric/_range_value_controller.py b/sinric/_range_value_controller.py index 570f433..92ffcb2 100644 --- a/sinric/_range_value_controller.py +++ b/sinric/_range_value_controller.py @@ -5,22 +5,29 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from numbers import Real +from typing import Any, Callable from ._sinricpro_constants import SinricProConstants +SetRangeCallbackType = Callable[[str, Real, str], tuple[bool, Real, str]] +# TODO: Should this return tuple[bool, Real]? +AdjustRangeCallbackType = Callable[[str, Real], Any] + class RangeValueController: - def __init__(self, range_value): - self.range_value = range_value - self.instance_id = '' + def __init__(self, range_value: Real): + self.range_value: Real = range_value + self.instance_id: str = '' - async def set_range_value(self, jsn, range_callback): + async def set_range_value(self, jsn: Any, range_callback: SetRangeCallbackType) -> tuple[bool, Real, str]: self.range_value = jsn.get("payload").get( SinricProConstants.VALUE).get(SinricProConstants.RANGE_VALUE) self.instance_id = jsn.get("payload").get( SinricProConstants.INSTANCE_ID, '') return range_callback(jsn.get("payload").get(SinricProConstants.DEVICEID), self.range_value, self.instance_id) - async def adjust_range_value(self, jsn, callback): + # TODO Match return type with callback function + async def adjust_range_value(self, jsn: Any, callback: AdjustRangeCallbackType) -> Any: self.range_value += jsn.get("payload").get( SinricProConstants.VALUE).get(SinricProConstants.RANGE_VALUE) if self.range_value > 100: From 407b783bcaab2e44a0e453f15ebef6856571926e Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:37:14 -0500 Subject: [PATCH 04/31] Commenting --- examples/blinds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/blinds.py b/examples/blinds.py index 8b45df0..be43768 100644 --- a/examples/blinds.py +++ b/examples/blinds.py @@ -7,8 +7,8 @@ from sinric import SinricPro, SinricProConstants BlindsCallbackFunctions = Union[ - Callable[[str, str], tuple[bool, str]], - Callable[[str, Real, str], tuple[bool, Real, str]] + Callable[[str, str], tuple[bool, str]], # Power state + Callable[[str, Real, str], tuple[bool, Real, str]] # Range Value ] APP_KEY: Final[str] = '' From 9d8722353358a8340421fc97f35a4fb63dc40cec Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:37:20 -0500 Subject: [PATCH 05/31] Typings for camera --- examples/camera.py | 51 +++++++++++++++++++---------- sinric/_camera_stream_controller.py | 15 ++++++--- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/examples/camera.py b/examples/camera.py index 6427272..becbc89 100644 --- a/examples/camera.py +++ b/examples/camera.py @@ -1,51 +1,66 @@ +from collections.abc import Callable +from typing import ByteString, Final, Optional, Union from sinric import SinricPro, SinricProConstants import requests import asyncio import base64 -APP_KEY = "" -APP_SECRET = "" -CAMERA_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +CAMERA_ID: Final[str] = '' -def get_webrtc_answer(device_id, offer): - sdp_offer = base64.b64decode(offer) +OfferType = Union[str, ByteString] + +CameraCallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], # Power state / Camera Stream Url + Callable[[str, OfferType], tuple[bool, Optional[bytes]]], # WEBRTC Answer + +] + + +def get_webrtc_answer(device_id: str, offer: OfferType) -> tuple[bool, Optional[bytes]]: + sdp_offer: Final[bytes] = base64.b64decode(offer) print('device_id: {} offer: {}'.format(device_id, offer)) # PORT 8889 for WebRTC. eg: for PiCam, use http://:8889/cam/whep - mediamtx_url = "http://:8889//whep" - headers = {"Content-Type": "application/sdp"} - response = requests.post(mediamtx_url, headers=headers, data=sdp_offer) + mediamtx_url: Final[str] = "http://:8889//whep" + headers: dict[str, str] = {"Content-Type": "application/sdp"} + response: requests.Response = requests.post( + mediamtx_url, headers=headers, data=sdp_offer) if response.status_code == 201: - answer = base64.b64encode(response.content).decode("utf-8") + answer: bytes = base64.b64encode(response.content).decode("utf-8") return True, answer else: - return False + return False, None -def power_state(device_id, state): +def power_state(device_id: str, state: str) -> tuple[bool, str]: print('device_id: {} power state: {}'.format(device_id, state)) return True, state -def get_camera_stream_url(device_id, protocol): + +def get_camera_stream_url(device_id: str, protocol: str) -> tuple[bool, str]: + # TODO: Should protocol be a string literal? # Google Home: RTSP protocol not supported. Requires a Chromecast TV or Google Nest Hub # Alexa: RTSP url must be interleaved TCP on port 443 (for both RTP and RTSP) over TLS 1.2 port 443 print('device_id: {} protocol: {}'.format(device_id, protocol)) if protocol == "rstp": - return True, 'rtsp://rtspurl:443' # RSTP. + return True, 'rtsp://rtspurl:443' # RSTP. else: - return True, 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8' # HLS + return True, 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8' # HLS + -callbacks = { +callbacks: dict[str, CameraCallbackFunctions] = { SinricProConstants.GET_WEBRTC_ANSWER: get_webrtc_answer, SinricProConstants.GET_CAMERA_STREAM_URL: get_camera_stream_url, SinricProConstants.SET_POWER_STATE: power_state } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [CAMERA_ID], callbacks, - enable_log=False, restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [CAMERA_ID], callbacks, + enable_log=False, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) diff --git a/sinric/_camera_stream_controller.py b/sinric/_camera_stream_controller.py index fcbcf32..137404e 100644 --- a/sinric/_camera_stream_controller.py +++ b/sinric/_camera_stream_controller.py @@ -5,18 +5,23 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from collections.abc import Callable +from typing import Any, ByteString, Optional, Union from ._sinricpro_constants import SinricProConstants +OfferType = Union[str, ByteString] + + class CameraStreamController: - async def get_webrtc_answer(self, jsn, get_webrtc_answer_callback): - self.offer = jsn.get("payload").get("value").get("offer") + async def get_webrtc_answer(self, jsn, get_webrtc_answer_callback: Callable[[str, OfferType], tuple[bool, Optional[bytes]]]) -> tuple[bool, Optional[bytes]]: + self.offer: OfferType = jsn.get("payload").get("value").get("offer") return get_webrtc_answer_callback(jsn.get("payload").get(SinricProConstants.DEVICEID), self.offer) - async def get_camera_stream_url(self, jsn, get_camera_stream_url_callback): + async def get_camera_stream_url(self, jsn: Any, get_camera_stream_url_callback: Callable[[str, str], tuple[bool, str]]) -> tuple[bool, str]: self.protocol = jsn.get("payload").get("value").get("protocol") - + return get_camera_stream_url_callback(jsn.get("payload").get(SinricProConstants.DEVICEID), - self.protocol) + self.protocol) From fd100367e44ae7d2436fdaf4727810e587028340 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:44:45 -0500 Subject: [PATCH 06/31] Typing for _mode_controller.py --- sinric/_mode_controller.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sinric/_mode_controller.py b/sinric/_mode_controller.py index eccff92..49fb435 100644 --- a/sinric/_mode_controller.py +++ b/sinric/_mode_controller.py @@ -5,13 +5,17 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from collections.abc import Callable +from typing import Any from ._sinricpro_constants import SinricProConstants class ModeController: def __init__(self): - self.instance_id = '' + self.instance_id: str = '' - async def set_mode(self, jsn, callback): - self.instance_id = jsn.get("payload").get(SinricProConstants.INSTANCE_ID, '') + async def set_mode(self, jsn, callback: Callable[[str, Any, str], tuple[bool, Any, str]]) \ + -> tuple[bool, Any, str]: + self.instance_id = jsn.get("payload").get( + SinricProConstants.INSTANCE_ID, '') return callback(jsn.get("payload").get(SinricProConstants.DEVICEID), jsn.get("payload").get('value').get('mode'), self.instance_id) From 89e1edb3178c5c9cf15907b7d1d7d7114486441e Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:44:55 -0500 Subject: [PATCH 07/31] Typing for custom_device.py --- examples/custom_device.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/custom_device.py b/examples/custom_device.py index a2ee218..9924274 100644 --- a/examples/custom_device.py +++ b/examples/custom_device.py @@ -1,34 +1,44 @@ +from collections.abc import Callable +from numbers import Real +from typing import Any, Final, Union from sinric import SinricPro, SinricProConstants import asyncio -APP_KEY = '' -APP_SECRET = '' -DEVICE_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +DEVICE_ID: Final[str] = '' +CallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], # Power state + Callable[[str, Real, str], tuple[bool, Real, str]], # Range Value + Callable[[str, Any, str], tuple[bool, Any, str]] # Mode Value +] -def power_state(device_id, state): + +def power_state(device_id: str, state: str) -> tuple[bool, str]: print(device_id, state) return True, state -def range_value(device_id, range_value, instance_id): +def range_value(device_id: str, range_value: Real, instance_id: str) -> tuple[bool, Real, str]: print(device_id, range_value, instance_id) return True, range_value, instance_id -def mode_value(device_id, mode_value, instance_id): +def mode_value(device_id: str, mode_value: Any, instance_id: str) -> tuple[bool, Any, str]: + # TODO: what is mode_Value? str?Enum? print(device_id, mode_value, instance_id) return True, mode_value, instance_id -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: power_state, SinricProConstants.SET_RANGE_VALUE: range_value, SinricProConstants.SET_MODE: mode_value } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [DEVICE_ID], callbacks, - enable_log=False, restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [DEVICE_ID], callbacks, + enable_log=False, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) From 695c28ea6a141c42ba0806990f9ce2baf9feb466 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:48:34 -0500 Subject: [PATCH 08/31] Calling all CallbackFunctions --- examples/blinds.py | 4 ++-- examples/camera.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/blinds.py b/examples/blinds.py index be43768..03e7767 100644 --- a/examples/blinds.py +++ b/examples/blinds.py @@ -6,7 +6,7 @@ from sinric import SinricPro, SinricProConstants -BlindsCallbackFunctions = Union[ +CallbackFunctions = Union[ Callable[[str, str], tuple[bool, str]], # Power state Callable[[str, Real, str], tuple[bool, Real, str]] # Range Value ] @@ -28,7 +28,7 @@ def set_range_value(device_id: str, value: Real, instance_id: str) -> tuple[bool return True, value, instance_id -callbacks: dict[str, BlindsCallbackFunctions] = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: set_power_state, SinricProConstants.SET_RANGE_VALUE: set_range_value } diff --git a/examples/camera.py b/examples/camera.py index becbc89..30ef99b 100644 --- a/examples/camera.py +++ b/examples/camera.py @@ -11,7 +11,7 @@ OfferType = Union[str, ByteString] -CameraCallbackFunctions = Union[ +CallbackFunctions = Union[ Callable[[str, str], tuple[bool, str]], # Power state / Camera Stream Url Callable[[str, OfferType], tuple[bool, Optional[bytes]]], # WEBRTC Answer @@ -53,7 +53,7 @@ def get_camera_stream_url(device_id: str, protocol: str) -> tuple[bool, str]: return True, 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8' # HLS -callbacks: dict[str, CameraCallbackFunctions] = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.GET_WEBRTC_ANSWER: get_webrtc_answer, SinricProConstants.GET_CAMERA_STREAM_URL: get_camera_stream_url, SinricProConstants.SET_POWER_STATE: power_state From 88437de1d41aa9c84eab70f6b4d65edcd203e096 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:52:21 -0500 Subject: [PATCH 09/31] Typings! --- examples/dim_switch.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/examples/dim_switch.py b/examples/dim_switch.py index 839dc4c..85c4a77 100644 --- a/examples/dim_switch.py +++ b/examples/dim_switch.py @@ -1,3 +1,6 @@ +from collections.abc import Callable +from numbers import Real +from typing import Union from sinric import SinricPro, SinricProConstants import asyncio @@ -5,26 +8,31 @@ APP_SECRET = '' DIM_SWITCH_ID = '' +CallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], # Power state + Callable[[str, Real], tuple[bool, Real]] # Power Level +] -def power_state(device_id, state): + +def power_state(device_id: str, state: str) -> tuple[bool, str]: print('device_id: {} state: {}'.format(device_id, state)) return True, state -def power_level(device_id, power_level): +def power_level(device_id: str, power_level: Real) -> tuple[bool, Real]: print('device_id: {} power level: {}'.format(device_id, power_level)) return True, power_level -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: power_state, SinricProConstants.SET_POWER_LEVEL: power_level } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [DIM_SWITCH_ID], callbacks, - enable_log=False, restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [DIM_SWITCH_ID], callbacks, + enable_log=False, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) # client.event_handler.raise_event(DIM_SWITCH_ID, SinricProConstants.SET_POWER_LEVEL, data = {SinricProConstants.POWER_LEVEL: 50 }) From 185d7424bbf26bbbfa1630d8b75ceafaae30de95 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 20:56:17 -0500 Subject: [PATCH 10/31] Typings and dict.get --- sinric/_power_level_controller.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sinric/_power_level_controller.py b/sinric/_power_level_controller.py index b63b136..20e6e5f 100644 --- a/sinric/_power_level_controller.py +++ b/sinric/_power_level_controller.py @@ -5,24 +5,28 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from numbers import Real +from typing import Any, Callable from ._sinricpro_constants import SinricProConstants class PowerLevelController: - def __init__(self, power_level) -> None: - self.power_level = power_level + def __init__(self, power_level: Real) -> None: + self.power_level: Real = power_level - async def set_power_level(self, jsn, power_level_callback): + async def set_power_level(self, jsn: Any, power_level_callback: Callable[[str, Real], tuple[bool, Real]]) -> tuple[bool, Real]: self.power_level = jsn.get("payload").get( SinricProConstants.VALUE).get(SinricProConstants.POWER_LEVEL) return power_level_callback(jsn.get("payload").get(SinricProConstants.DEVICEID), self.power_level) - async def adjust_power_level(self, jsn, adjust_power_level_cb): - self.power_level += jsn[SinricProConstants.VALUE][SinricProConstants.POWER_LEVEL_DELTA] + async def adjust_power_level(self, jsn: Any, adjust_power_level_cb: Callable[[str, Real], Any]) -> Any: + # TODO: no examples for adjust_power_level_cb can't infer return type + self.power_level += jsn.get(SinricProConstants.VALUE).get( + SinricProConstants.POWER_LEVEL_DELTA) if self.power_level > 100: self.power_level = 100 elif self.power_level < 0: self.power_level = 0 - return adjust_power_level_cb(jsn[SinricProConstants.DEVICEID], + return adjust_power_level_cb(jsn.get(SinricProConstants.DEVICEID), self.power_level) From be872d5b4400183de7cf9163fededaadedd8ba72 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 21:14:31 -0500 Subject: [PATCH 11/31] Made consts --- examples/dim_switch.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/dim_switch.py b/examples/dim_switch.py index 85c4a77..cde9aae 100644 --- a/examples/dim_switch.py +++ b/examples/dim_switch.py @@ -1,12 +1,12 @@ from collections.abc import Callable from numbers import Real -from typing import Union +from typing import Final, Union from sinric import SinricPro, SinricProConstants import asyncio -APP_KEY = '' -APP_SECRET = '' -DIM_SWITCH_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +DIM_SWITCH_ID: Final[str] = '' CallbackFunctions = Union[ Callable[[str, str], tuple[bool, str]], # Power state From 4374fe960b8017a40d26cd074b724fe2e7cf8320 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 21:16:56 -0500 Subject: [PATCH 12/31] Typings on doorbell.py --- examples/doorbell.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/doorbell.py b/examples/doorbell.py index 05d5493..c7c4949 100644 --- a/examples/doorbell.py +++ b/examples/doorbell.py @@ -1,9 +1,10 @@ +from typing import Final from sinric import SinricPro, SinricProConstants import asyncio -APP_KEY = '' -APP_SECRET = '' -DOORBELL_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +DOORBELL_ID: Final[str] = '' ''' DON'T FORGET TO TURN ON 'Doorbell Press' IN ALEXA APP @@ -19,9 +20,9 @@ async def events(): callbacks = {} if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [DOORBELL_ID], callbacks, event_callbacks=events, - enable_log=True, restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [DOORBELL_ID], callbacks, event_callbacks=events, + enable_log=True, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) # client.event_handler.raise_event(DOORBELL_ID, SinricProConstants.DOORBELLPRESS) From 8a1125b5eadbf2c55e794ebd683bc297c5c3e41e Mon Sep 17 00:00:00 2001 From: cojmeister Date: Thu, 28 Dec 2023 21:20:33 -0500 Subject: [PATCH 13/31] Typings on garagedoor.py --- examples/garagedoor.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/garagedoor.py b/examples/garagedoor.py index 17dd335..594c786 100644 --- a/examples/garagedoor.py +++ b/examples/garagedoor.py @@ -1,30 +1,38 @@ -from sinric import SinricPro, SinricProConstants import asyncio +from collections.abc import Callable +from typing import Any, Final, Union + +from sinric import SinricPro, SinricProConstants + +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +GARAGEDOOR_ID: Final[str] = '' -APP_KEY = '' -APP_SECRET = '' -GARAGEDOOR_ID = '' +CallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], # Power state + Callable[[str, Any, str], tuple[bool, Any, str]] # Mode Value +] -def power_state(device_id, state): +def power_state(device_id: str, state: str) -> tuple[bool, str]: print('device_id: {} state: {}'.format(device_id, state)) return True, device_id -def set_mode(device_id, state, instance_id): +def set_mode(device_id: str, state: Any, instance_id: str) -> tuple[bool, Any, str]: print('device_id: {} mode: {}'.format(device_id, state)) return True, state, instance_id -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_MODE: set_mode, SinricProConstants.SET_POWER_STATE: power_state } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [GARAGEDOOR_ID], callbacks, - enable_log=False, restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [GARAGEDOOR_ID], callbacks, + enable_log=False, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) # client.event_handler.raise_event(GARAGEDOOR_ID, SinricProConstants.SET_MODE, data = {SinricProConstants.MODE: SinricProConstants.OPEN }) From 87f320322cbace253596f6528aa230a1ce6a8fb6 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sun, 31 Dec 2023 05:59:54 -0500 Subject: [PATCH 14/31] Typings for light and all relevant controllers --- examples/light.py | 44 +++++++++++++++++++++----------- sinric/_brightness_controller.py | 19 +++++++++----- sinric/_color_controller.py | 11 +++++--- sinric/_color_temperature.py | 17 +++++++----- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/examples/light.py b/examples/light.py index d639edd..3046169 100644 --- a/examples/light.py +++ b/examples/light.py @@ -1,48 +1,62 @@ -from sinric import SinricPro, SinricProConstants import asyncio +from collections.abc import Callable +from numbers import Real +from typing import Final, Union + +from sinric import SinricPro, SinricProConstants + +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +LIGHT_ID: Final[str] = '' -APP_KEY = '' -APP_SECRET = '' -LIGHT_ID = '' +CallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], # Power state + # Set Brightness / Adjust Brightness + Callable[[str, Real], tuple[bool, str]], + Callable[[str, int, int, int], bool], # Set Color + Callable[[str, Real], bool], # Set Color Temperature + Callable[[str, Real], tuple[bool, Real]], # Adjust Color Temperature +] -def power_state(device_id, state): +def power_state(device_id: str, state: str) -> tuple[bool, str]: print('device_id: {} state: {}'.format(device_id, state)) return True, state -def set_brightness(device_id, brightness): +def set_brightness(device_id: str, brightness: Real) -> tuple[bool, Real]: print('device_id: {} brightness: {}'.format(device_id, brightness)) return True, brightness -def adjust_brightness(device_id, brightness): +def adjust_brightness(device_id: str, brightness: Real) -> tuple[bool, Real]: print('device_id: {} adjusted brightness level: {}'.format(device_id, brightness)) return True, brightness -def set_color(device_id, r, g, b): +# TODO: is there a bit type (0-256)? +def set_color(device_id: str, r: int, g: int, b: int) -> bool: print('device_id: {} R:{},G:{},B:{}'.format(device_id, r, g, b)) return True -def set_color_temperature(device_id, color_temperature): +def set_color_temperature(device_id: str, color_temperature: Real) -> bool: print('device_id: {} color temperature:{}'.format( device_id, color_temperature)) return True -def increase_color_temperature(device_id, value): +def increase_color_temperature(device_id: str, value: Real) -> tuple[bool, Real]: print('device_id: {} value:{}'.format(device_id, value)) return True, value -def decrease_color_temperature(device_id, value): +def decrease_color_temperature(device_id: str, value: Real) -> tuple[bool, Real]: print('device_id: {} value:{}'.format(device_id, value)) return True, value -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: power_state, SinricProConstants.SET_BRIGHTNESS: set_brightness, SinricProConstants.ADJUST_BRIGHTNESS: adjust_brightness, @@ -53,9 +67,9 @@ def decrease_color_temperature(device_id, value): } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [LIGHT_ID], callbacks, enable_log=False, - restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [LIGHT_ID], callbacks, + enable_log=False, restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) # To update the light state on server. diff --git a/sinric/_brightness_controller.py b/sinric/_brightness_controller.py index d6816ad..479acdb 100644 --- a/sinric/_brightness_controller.py +++ b/sinric/_brightness_controller.py @@ -5,18 +5,25 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from collections.abc import Callable +from numbers import Real +from typing import Any + from ._sinricpro_constants import SinricProConstants + class BrightnessController: - def __init__(self, brightness_level) -> None: - self.brightness_level = brightness_level + def __init__(self, brightness_level: Real) -> None: + self.brightness_level: Real = brightness_level - async def set_brightness(self, jsn, brightness_callback): - self.brightness_level = jsn.get("payload").get(SinricProConstants.VALUE).get(SinricProConstants.BRIGHTNESS) + async def set_brightness(self, jsn: Any, brightness_callback: Callable[[str, Real], tuple[bool, str]]) -> tuple[bool, str]: + self.brightness_level = jsn.get("payload").get( + SinricProConstants.VALUE).get(SinricProConstants.BRIGHTNESS) return brightness_callback(jsn.get("payload").get(SinricProConstants.DEVICEID), self.brightness_level) - async def adjust_brightness(self, jsn, brightness_callback): - self.brightness_level += jsn.get("payload").get(SinricProConstants.VALUE).get(SinricProConstants.BRIGHTNESS_DELTA) + async def adjust_brightness(self, jsn, brightness_callback: Callable[[str, Real], tuple[bool, str]]) -> tuple[bool, str]: + self.brightness_level += jsn.get("payload").get( + SinricProConstants.VALUE).get(SinricProConstants.BRIGHTNESS_DELTA) if self.brightness_level > 100: self.brightness_level = 100 elif self.brightness_level < 0: diff --git a/sinric/_color_controller.py b/sinric/_color_controller.py index e8bfc6e..00f68b6 100644 --- a/sinric/_color_controller.py +++ b/sinric/_color_controller.py @@ -6,10 +6,15 @@ """ +from collections.abc import Callable + + class ColorController: - async def set_color(self, jsn, set_color_callback): + async def set_color(self, jsn, set_color_callback: Callable[[str, int, int, int], bool]) -> bool: return set_color_callback(jsn.get("payload").get("deviceId"), - jsn.get("payload").get("value").get("color").get("r"), - jsn.get("payload").get("value").get("color").get("g"), + jsn.get("payload").get( + "value").get("color").get("r"), + jsn.get("payload").get( + "value").get("color").get("g"), jsn.get("payload").get("value").get("color").get("b")) diff --git a/sinric/_color_temperature.py b/sinric/_color_temperature.py index 7695a80..9547d16 100644 --- a/sinric/_color_temperature.py +++ b/sinric/_color_temperature.py @@ -6,17 +6,22 @@ """ +from collections.abc import Callable +from numbers import Real +from typing import Any + + class ColorTemperatureController: - def __init__(self, temperatures_index, temperatures_array): - self.temperatures_index = temperatures_index - self.temperatures_array = temperatures_array + def __init__(self, temperatures_index: Real, temperatures_array: list[Real]) -> None: + self.temperatures_index: Real = temperatures_index + self.temperatures_array: list[Real] = temperatures_array - async def set_color_temperature(self, jsn, set_callback): + async def set_color_temperature(self, jsn: Any, set_callback: Callable[[str, Real], bool]) -> bool: return set_callback(jsn.get("payload").get("deviceId"), jsn.get("payload").get("value").get("colorTemperature")) - async def increase_color_temperature(self, jsn, increase_callback): + async def increase_color_temperature(self, jsn: Any, increase_callback: Callable[[str, Real], tuple[bool, Real]]) -> tuple[bool, Real]: return increase_callback(jsn.get("payload").get("deviceId"), jsn.get("payload").get("value")) - async def decrease_color_temperature(self, jsn, decrease_callback): + async def decrease_color_temperature(self, jsn: Any, decrease_callback: Callable[[str, Real], tuple[bool, Real]]) -> tuple[bool, Real]: return decrease_callback(jsn.get("payload").get("deviceId"), jsn.get("payload").get("value")) From a305bf57fab2519f3d3ea787da42490f4652f1b1 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sun, 31 Dec 2023 06:09:21 -0500 Subject: [PATCH 15/31] Typings for smart lock --- examples/smart_lock.py | 21 +++++++++++++-------- sinric/_lock_controller.py | 7 +++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/smart_lock.py b/examples/smart_lock.py index d4665e8..d66188a 100644 --- a/examples/smart_lock.py +++ b/examples/smart_lock.py @@ -1,24 +1,29 @@ +from collections.abc import Callable +from typing import Final from sinric import SinricPro, SinricProConstants import asyncio -APP_KEY = '' -APP_SECRET = '' -LOCK_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +LOCK_ID: Final[str] = '' +CallbackFunctions = Callable[[str, str], tuple[bool, str]] -def lock_state(device_id, state): + +def lock_state(device_id: str, state: str) -> tuple[bool, str]: + # TODO: state should definitely be an enum print('device_id: {} lock state: {}'.format(device_id, state)) return True, state -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_LOCK_STATE: lock_state } if __name__ == '__main__': - loop = asyncio.get_event_loop() - client = SinricPro(APP_KEY, [LOCK_ID], callbacks, enable_log=False, - restore_states=False, secret_key=APP_SECRET) + loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() + client: SinricPro = SinricPro(APP_KEY, [LOCK_ID], callbacks, enable_log=False, + restore_states=False, secret_key=APP_SECRET) loop.run_until_complete(client.connect()) diff --git a/sinric/_lock_controller.py b/sinric/_lock_controller.py index 6e20ee0..0b97025 100644 --- a/sinric/_lock_controller.py +++ b/sinric/_lock_controller.py @@ -5,11 +5,14 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from collections.abc import Callable +from typing import Any from ._sinricpro_constants import SinricProConstants + class LockStateController: def __init__(self): - self.lock_state = '' + self.lock_state: str = '' - async def set_lock_state(self, jsn, callback): + async def set_lock_state(self, jsn: Any, callback: Callable[[str, str], tuple[bool, str]]) -> tuple[bool, str]: return callback(jsn.get(SinricProConstants.DEVICEID), jsn.get("payload").get('value', False).get(SinricProConstants.STATE, "unlock")) From 22be9e505a8612ee02ce168cf2f40da95cd233ad Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sun, 31 Dec 2023 06:27:35 -0500 Subject: [PATCH 16/31] Added types files --- sinric/__init__.py | 5 +++-- sinric/_types.py | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 sinric/_types.py diff --git a/sinric/__init__.py b/sinric/__init__.py index 60a1894..e0eeca0 100644 --- a/sinric/__init__.py +++ b/sinric/__init__.py @@ -8,5 +8,6 @@ from ._sinricpro import SinricPro from ._sinricpro_udp import SinricProUdp from ._events import Events -from . import Events -from ._sinricpro_constants import SinricProConstants \ No newline at end of file +from . import Events +from ._sinricpro_constants import SinricProConstants +from ._types import SinricProTypes diff --git a/sinric/_types.py b/sinric/_types.py new file mode 100644 index 0000000..0542df7 --- /dev/null +++ b/sinric/_types.py @@ -0,0 +1,7 @@ +# Types: +from numbers import Real +from typing import Union + + +class SinricProTypes(object): + BandDictType = dict[str, Union[str, Real]] From f4d0caa7f5d95b763842e28ca0704ebfe07da44c Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sun, 31 Dec 2023 06:27:42 -0500 Subject: [PATCH 17/31] Began Speaker --- examples/speaker.py | 21 +++++++++++++++------ sinric/_speaker_controller.py | 9 +++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/examples/speaker.py b/examples/speaker.py index 6604d05..5193a25 100644 --- a/examples/speaker.py +++ b/examples/speaker.py @@ -1,17 +1,26 @@ -from sinric import SinricPro, SinricProConstants +from collections.abc import Callable +from numbers import Real +from typing import Final, Union +from sinric import SinricPro, SinricProConstants, SinricProTypes import asyncio -APP_KEY = '' -APP_SECRET = '' -SPEAKER_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +SPEAKER_ID: Final[str] = '' +CallbackFunctions = Union[ + Callable[[str, str], tuple[bool, str]], # Power state + Callable[[str, str, Real], tuple[bool, + SinricProTypes.BandDictType]], # Set Bands +] -def power_state(device_id, state): + +def power_state(device_id: str, state: str) -> tuple[bool, str]: print('device_id: {} lock state: {}'.format(device_id, state)) return True, state -def set_bands(device_id, name, level): +def set_bands(device_id: str, name: str, level: Real) -> tuple[bool, SinricProTypes.BandDictType]: print('device_id: {}, name: {}, name: {}'.format(device_id, name, level)) return True, {'name': name, 'level': level} diff --git a/sinric/_speaker_controller.py b/sinric/_speaker_controller.py index a207c91..24189d7 100644 --- a/sinric/_speaker_controller.py +++ b/sinric/_speaker_controller.py @@ -5,15 +5,20 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from collections.abc import Callable +from numbers import Real +from typing import Union from ._sinricpro_constants import SinricProConstants +from ._types import SinricProTypes class SpeakerController: def __init__(self): - self.band = 0 + self.band: Real = 0 - async def set_bands(self, jsn, callback): + async def set_bands(self, jsn, callback: Callable[[str, str, Real], tuple[bool, SinricProTypes.BandDictType]]) \ + -> tuple[bool, SinricProTypes.BandDictType]: value = jsn.get("payload").get(SinricProConstants.VALUE) bands = value.get(SinricProConstants.BANDS)[0] return callback(jsn.get("payload").get(SinricProConstants.DEVICEID), bands.get(SinricProConstants.NAME), bands.get(SinricProConstants.LEVEL)) From a3671f2f39a934a484e78a82c3e142357e5fafd9 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sun, 31 Dec 2023 07:45:27 -0500 Subject: [PATCH 18/31] Typings for SinricPro --- sinric/_sinricpro.py | 42 +++++++++++++++++++++++------------------- sinric/_types.py | 5 ++++- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/sinric/_sinricpro.py b/sinric/_sinricpro.py index a48538b..f2ae891 100644 --- a/sinric/_sinricpro.py +++ b/sinric/_sinricpro.py @@ -12,10 +12,12 @@ from loguru import logger from ._sinricpro_websocket import SinricProSocket from ._events import Events +from ._types import SinricProTypes from typing import ( Any, Awaitable, Callable, + Final, Iterable as TypingIterable, List, Optional, @@ -29,40 +31,42 @@ class SinricPro: - def __init__(self, api, device_id, request_callbacks, event_callbacks=None, enable_log=False, restore_states=False, - secret_key="", loop_delay=0.5): + def __init__(self, api: str, device_id: list[str], request_callbacks: SinricProTypes.RequestCallbacks, + event_callbacks: Optional[SinricProTypes.EventCallbacks] = None, enable_log: bool = False, + restore_states: bool = False, secret_key: str = "", loop_delay: float = 0.5): try: assert (self.verify_device_ids(device_id)) - self.restore_states = restore_states - self.app_key = api - self.loop_delay = loop_delay if loop_delay > 0 else 0.5 - self.secret_key = secret_key - self.device_id = device_id + self.restore_states: Final[bool] = restore_states + self.app_key: Final[str] = api + self.loop_delay: Final[float] = loop_delay if loop_delay > 0 else 0.5 + self.secret_key: Final[str] = secret_key + self.device_id: Final[list[str]] = device_id self.logger = logger - self.request_callbacks = request_callbacks - self.socket = SinricProSocket(self.app_key, self.device_id, self.request_callbacks, enable_log, self.logger, - self.restore_states, self.secret_key, loop_delay=loop_delay) + self.request_callbacks: Final[SinricProTypes.RequestCallbacks] = request_callbacks + self.socket: SinricProSocket = SinricProSocket(self.app_key, self.device_id, self.request_callbacks, enable_log, self.logger, + self.restore_states, self.secret_key, loop_delay=loop_delay) self.connection = None - self.event_callbacks = event_callbacks - self.event_handler = Events( + self.event_callbacks: Optional[SinricProTypes.EventCallbacks] = event_callbacks + self.event_handler: Events = Events( self.connection, self.logger, self.secret_key) except AssertionError as e: logger.error("Device Id verification failed") sys.exit(0) - def verify_device_ids(self, device_id_arr): - arr = device_id_arr + def verify_device_ids(self, device_id_arr: list[str]) -> bool: + arr: list[str] = device_id_arr for i in arr: res = re.findall(r'^[a-fA-F0-9]{24}$', i) if len(res) == 0: return False return True - async def connect(self, udp_client=None, sleep=0): + async def connect(self, udp_client=None, sleep: int = 0) -> None: try: self.connection = await self.socket.connect() - receive_message_task = asyncio.create_task(self.socket.receive_message(connection=self.connection)) + receive_message_task = asyncio.create_task( + self.socket.receive_message(connection=self.connection)) handle_queue_task = asyncio.create_task(self.socket.handle_queue()) if self.event_callbacks is not None: @@ -83,14 +87,14 @@ async def connect(self, udp_client=None, sleep=0): # if loop is None: # loop = asyncio.new_event_loop() - + # #loop.set_debug(debug) # self.connection = await self.socket.connect() # receive_message_task = loop.create_task(self.socket.receive_message(connection=self.connection)) # handle_queue_task = loop.create_task(self.socket.handle_queue()) # #handle_event_queue_task = asyncio.create_task(self.event_callbacks()) - + # try: # asyncio.set_event_loop(loop) # loop.run_until_complete([receive_message_task, handle_queue_task]) @@ -101,4 +105,4 @@ async def connect(self, udp_client=None, sleep=0): # #_cancel_tasks(asyncio.all_tasks(loop), loop) # loop.run_until_complete(loop.shutdown_asyncgens()) # loop.close() - # asyncio.set_event_loop(None) \ No newline at end of file + # asyncio.set_event_loop(None) diff --git a/sinric/_types.py b/sinric/_types.py index 0542df7..a6540a5 100644 --- a/sinric/_types.py +++ b/sinric/_types.py @@ -1,7 +1,10 @@ -# Types: +from collections.abc import Callable from numbers import Real from typing import Union class SinricProTypes(object): + RequestCallbacks = dict[str, Callable] + EventCallbacks = Callable[[]] + BandDictType = dict[str, Union[str, Real]] From 7ea89899b56cd19fb800faa763e12fd862b7d3c8 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Tue, 2 Jan 2024 08:18:10 -0500 Subject: [PATCH 19/31] Added typings --- sinric/_sinricpro.py | 11 ++++---- sinric/_sinricpro_websocket.py | 48 +++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/sinric/_sinricpro.py b/sinric/_sinricpro.py index f2ae891..88fa9aa 100644 --- a/sinric/_sinricpro.py +++ b/sinric/_sinricpro.py @@ -9,7 +9,7 @@ import re import sys -from loguru import logger +from loguru import Logger, logger from ._sinricpro_websocket import SinricProSocket from ._events import Events from ._types import SinricProTypes @@ -20,6 +20,7 @@ Final, Iterable as TypingIterable, List, + NoReturn, Optional, Set, Type, @@ -41,10 +42,10 @@ def __init__(self, api: str, device_id: list[str], request_callbacks: SinricProT self.loop_delay: Final[float] = loop_delay if loop_delay > 0 else 0.5 self.secret_key: Final[str] = secret_key self.device_id: Final[list[str]] = device_id - self.logger = logger + self.logger: Final[Logger] = logger self.request_callbacks: Final[SinricProTypes.RequestCallbacks] = request_callbacks - self.socket: SinricProSocket = SinricProSocket(self.app_key, self.device_id, self.request_callbacks, enable_log, self.logger, - self.restore_states, self.secret_key, loop_delay=loop_delay) + self.socket: Final[SinricProSocket] = SinricProSocket(self.app_key, self.device_id, self.request_callbacks, enable_log, self.logger, + self.restore_states, self.secret_key, loop_delay=loop_delay) self.connection = None self.event_callbacks: Optional[SinricProTypes.EventCallbacks] = event_callbacks self.event_handler: Events = Events( @@ -62,7 +63,7 @@ def verify_device_ids(self, device_id_arr: list[str]) -> bool: return False return True - async def connect(self, udp_client=None, sleep: int = 0) -> None: + async def connect(self, udp_client=None, sleep: int = 0) -> NoReturn: try: self.connection = await self.socket.connect() receive_message_task = asyncio.create_task( diff --git a/sinric/_sinricpro_websocket.py b/sinric/_sinricpro_websocket.py index 6b4bfd7..6e51169 100644 --- a/sinric/_sinricpro_websocket.py +++ b/sinric/_sinricpro_websocket.py @@ -5,35 +5,40 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +import asyncio +import json +from time import time +from typing import Final, NoReturn +from loguru import Logger + +import pkg_resources from websockets import client from websockets.exceptions import ConnectionClosed -import json -from .helpers.wait import waitAsync -from ._queues import queue from ._callback_handler import CallBackHandler +from ._queues import queue from ._signature import Signature -from time import time -import asyncio -import pkg_resources +from ._types import SinricProTypes +from .helpers.wait import waitAsync class SinricProSocket(Signature): - def __init__(self, app_key, device_id, callbacks, enable_trace=False, logger=None, restore_states=False, - secret_key="", loop_delay=0.5): - self.app_key = app_key - self.secret_key = secret_key - self.restore_states = restore_states - self.logger = logger - self.device_ids = device_id + def __init__(self, app_key: str, device_id: str, callbacks: SinricProTypes.RequestCallbacks, + enable_trace: bool = False, logger: Logger = None, restore_states: bool = False, + secret_key: str = "", loop_delay: float = 0.5): + self.app_key: Final[str] = app_key + self.secret_key: Final[str] = secret_key + self.restore_states: Final[bool] = restore_states + self.logger: Final[Logger] = logger + self.device_ids: Final[list[str]] = device_id self.connection = None - self.callbacks = callbacks - self.loop_delay = loop_delay + self.callbacks: SinricProTypes.RequestCallbacks = callbacks + self.loop_delay: Final[float] = loop_delay - self.callbackHandler = CallBackHandler(self.callbacks, enable_trace, self.logger, self.restore_states, - secret_key=self.secret_key) - self.enableTrace = enable_trace + self.callbackHandler: CallBackHandler = CallBackHandler(self.callbacks, enable_trace, self.logger, self.restore_states, + secret_key=self.secret_key) + self.enableTrace: Final[bool] = enable_trace Signature.__init__(self, self.secret_key) async def connect(self): # Producer @@ -50,10 +55,11 @@ async def connect(self): # Producer self.logger.success("Connected :)") timestamp = await self.connection.recv() if (int(time()) - json.loads(timestamp).get('timestamp') > 60000): - self.logger.warning('Timestamp is not in sync. Please check your system time.') + self.logger.warning( + 'Timestamp is not in sync. Please check your system time.') return self.connection - async def receive_message(self, connection): + async def receive_message(self, connection) -> NoReturn: while True: try: message = await waitAsync(connection.recv()) @@ -68,7 +74,7 @@ async def receive_message(self, connection): self.logger.info('Connection with server closed') raise e - async def handle_queue(self): + async def handle_queue(self) -> NoReturn: while True: await asyncio.sleep(self.loop_delay) From 25d79728190f605a6d4939b7119dc0d977455953 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Tue, 2 Jan 2024 08:36:47 -0500 Subject: [PATCH 20/31] Began adding typings to event and hanlder --- sinric/_callback_handler.py | 235 +++++++++++++++++++++--------------- sinric/_events.py | 59 ++++++--- 2 files changed, 176 insertions(+), 118 deletions(-) diff --git a/sinric/_callback_handler.py b/sinric/_callback_handler.py index cc3cf97..fb4f358 100644 --- a/sinric/_callback_handler.py +++ b/sinric/_callback_handler.py @@ -25,13 +25,16 @@ from ._lock_controller import LockStateController from ._signature import Signature from ._leaky_bucket import LeakyBucket -from ._sinricpro_constants import SinricProConstants +from ._sinricpro_constants import SinricProConstants +from ._types import SinricProTypes # noinspection PyBroadException + + class CallBackHandler(PowerLevelController, PowerController, BrightnessController, ColorController, ColorTemperatureController, ThermostateMode, RangeValueController, TemperatureController, TvController, SpeakerController, LockStateController, ModeController, CameraStreamController, Signature): - def __init__(self, callbacks, trace_bool, logger, enable_track=False, secret_key=""): + def __init__(self, callbacks: SinricProTypes.RequestCallbacks, trace_bool: bool, logger, enable_track=False, secret_key=""): self.myHmac = None self.secret_key = secret_key self.bucket = LeakyBucket(10, 1000, 60000) @@ -49,7 +52,8 @@ def __init__(self, callbacks, trace_bool, logger, enable_track=False, secret_key Signature.__init__(self, self.secret_key) SpeakerController.__init__(self) ModeController.__init__(self) - ColorTemperatureController.__init__(self, 0, [2200, 2700, 4000, 5500, 7000]) + ColorTemperatureController.__init__( + self, 0, [2200, 2700, 4000, 5500, 7000]) CameraStreamController.__init__(self) self.callbacks = callbacks @@ -66,8 +70,9 @@ async def handle_response(response, connection, udp_client): self.logger.info(f"Response : {dumps(response)}") if response_cmd == 'socket_response': await connection.send(dumps(response)) - elif response_cmd == 'udp_response' and udp_client != None : - udp_client.sendResponse(dumps(response).encode('ascii'), data_array[2]) + elif response_cmd == 'udp_response' and udp_client != None: + udp_client.sendResponse( + dumps(response).encode('ascii'), data_array[2]) def json_response(action, resp, data_dict, instance_id='') -> dict: header = { @@ -85,7 +90,7 @@ def json_response(action, resp, data_dict, instance_id='') -> dict: "type": "response", "value": data_dict } - + if instance_id: payload['instanceId'] = instance_id @@ -93,10 +98,11 @@ def json_response(action, resp, data_dict, instance_id='') -> dict: return {"header": header, "payload": payload, "signature": signature} - if message_type == 'request_response' : - assert (self.verify_signature(jsn.get('payload'), jsn.get("signature").get("HMAC"))) - action = jsn.get('payload').get('action') - + if message_type == 'request_response': + assert (self.verify_signature(jsn.get('payload'), + jsn.get("signature").get("HMAC"))) + action = jsn.get('payload').get('action') + if action == SinricProConstants.SET_POWER_STATE: await self._handle_set_power_state(connection, udp_client, jsn, handle_response, json_response, action) @@ -168,22 +174,22 @@ def json_response(action, resp, data_dict, instance_id='') -> dict: elif action == SinricProConstants.RESET_BANDS: await self._handle_reset_bands(connection, udp_client, jsn, handle_response, json_response, action) - + elif action == SinricProConstants.SET_MODE: await self._handle_set_mode(connection, udp_client, jsn, handle_response, json_response, action) - + elif action == SinricProConstants.SET_LOCK_STATE: await self._handle_set_lock_state(connection, udp_client, jsn, handle_response, json_response, action) - + elif action == SinricProConstants.GET_WEBRTC_ANSWER: await self._handle_get_webrtc_answer(connection, udp_client, jsn, handle_response, json_response, action) elif action == SinricProConstants.GET_CAMERA_STREAM_URL: await self._handle_get_camera_stream_url(connection, udp_client, jsn, handle_response, json_response, action) - # Handle events + # Handle events - if message_type == 'event' : + if message_type == 'event': if response_cmd == SinricProConstants.DOORBELLPRESS: if self.bucket.add_drop(): self.logger.info('Sending Doorbell event') @@ -287,36 +293,41 @@ def json_response(action, resp, data_dict, instance_id='') -> dict: async def _handle_get_camera_stream_url(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, url = await self.get_camera_stream_url(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "url": url }) + response = json_response(action, resp, {"url": url}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_get_webrtc_answer(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.get_webrtc_answer(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "answer": value }) + response = json_response(action, resp, {"answer": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_set_lock_state(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.set_lock_state(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "state": value.upper() + 'ED' }) #TODO: Fix this later + # TODO: Fix this later + response = json_response( + action, resp, {"state": value.upper() + 'ED'}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) @@ -324,13 +335,14 @@ async def _handle_set_mode(self, connection, udp_client, jsn, handle_response, j try: resp, value, instance_id = await self.set_mode(jsn, self.callbacks.get(action)) response = json_response(action, resp, { - "mode": value - }, instance_id) + "mode": value + }, instance_id) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) @@ -338,24 +350,25 @@ async def _handle_reset_bands(self, connection, udp_client, jsn, handle_response try: resp = await self.reset_bands(jsn, self.callbacks.get(action)) response = json_response(action, resp, { - "bands": [ - { - "name": "BASS", + "bands": [ + { + "name": "BASS", "level": 0 - }, - { - "name": "MIDRANGE", + }, + { + "name": "MIDRANGE", "level": 0 - }, - { - "name": "TREBLE", + }, + { + "name": "TREBLE", "level": 0 - }] - }) + }] + }) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) @@ -363,18 +376,19 @@ async def _handle_adjust_bands(self, connection, udp_client, jsn, handle_respons try: resp, value = await self.adjust_bands(jsn, self.callbacks.get(action)) response = json_response(action, resp, { - "bands": [ - { - "name": value.get('name'), - "level": value.get('level') - } - ] - }) + "bands": [ + { + "name": value.get('name'), + "level": value.get('level') + } + ] + }) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) @@ -382,152 +396,167 @@ async def _handle_set_bands(self, connection, udp_client, jsn, handle_response, try: resp, value = await self.set_bands(jsn, self.callbacks.get(action)) response = json_response(action, resp, { - "bands": [ - { - "name": value.get('name'), - "level": value.get('level') - } - ] - }) + "bands": [ + { + "name": value.get('name'), + "level": value.get('level') + } + ] + }) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_set_mute(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.set_mute(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "mute": value }) + response = json_response(action, resp, {"mute": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def handle_skip_channel(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.skip_channels(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "channel": { "name": value } }) + response = json_response( + action, resp, {"channel": {"name": value}}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_change_channel(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.change_channel(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "channel": { "name": value }}) + response = json_response( + action, resp, {"channel": {"name": value}}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_select_input(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.select_input(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "input": value }) + response = json_response(action, resp, {"input": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_media_control(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.media_control(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "control": value }) + response = json_response(action, resp, {"control": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_adjust_volume(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.adjust_volume(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "volume": value }) + response = json_response(action, resp, {"volume": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_set_volume(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.set_volume(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "volume": value }) + response = json_response(action, resp, {"volume": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_adjust_target_tempreature(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.adjust_temperature(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "temperature": value }) + response = json_response(action, resp, {"temperature": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_target_tempreature(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.target_temperature(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "temperature": value }) + response = json_response(action, resp, {"temperature": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_adjust_range_value(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.adjust_range_value(jsn, self.callbacks.get('adjustRangeValue')) - response = json_response(action, resp, { "rangeValue": value }) + response = json_response(action, resp, {"rangeValue": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_set_range_value(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value, instance_id = await self.set_range_value(jsn, self.callbacks.get(action)) - response = json_response(action, resp, { "rangeValue": value }, instance_id) + response = json_response( + action, resp, {"rangeValue": value}, instance_id) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) async def _handle_set_thermostat_mode(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.set_thermostate_mode(jsn, self.callbacks[action]) - response = json_response(action, resp, { "thermostatMode": value }) - + response = json_response(action, resp, {"thermostatMode": value}) + if resp: await handle_response(response, connection, udp_client) except Exception as e: @@ -536,36 +565,40 @@ async def _handle_set_thermostat_mode(self, connection, udp_client, jsn, handle_ async def _handle_decrease_color_tempreature(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.decrease_color_temperature(jsn, self.callbacks[action]) - response = json_response(action, resp, { "colorTemperature": value}) + response = json_response(action, resp, {"colorTemperature": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') async def _handle_increase_color_tempreature(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.increase_color_temperature(jsn, self.callbacks[action]) - response = json_response(action, resp, { "colorTemperature": value }) + response = json_response(action, resp, {"colorTemperature": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') async def _handle_set_color_tempreature(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp = await self.set_color_temperature(jsn, self.callbacks[action]) - response = json_response(action, resp, { "colorTemperature": jsn.get("payload").get("value").get("colorTemperature") }) + response = json_response(action, resp, {"colorTemperature": jsn.get( + "payload").get("value").get("colorTemperature")}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') @@ -573,29 +606,31 @@ async def _handle_set_color(self, connection, udp_client, jsn, handle_response, try: resp = await self.set_color(jsn, self.callbacks[action]) response = json_response(action=action, resp=resp, data_dict={ - "color": { - "b": jsn.get("payload").get("value").get("color").get("b"), - "g": jsn.get("payload").get("value").get("color").get("g"), - "r": jsn.get("payload").get("value").get("color").get("r") - } - }) + "color": { + "b": jsn.get("payload").get("value").get("color").get("b"), + "g": jsn.get("payload").get("value").get("color").get("g"), + "r": jsn.get("payload").get("value").get("color").get("r") + } + }) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') async def _handle_adjust_brightness(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.adjust_brightness(jsn, self.callbacks[action]) - response = json_response(action, resp, { "brightness": value }) + response = json_response(action, resp, {"brightness": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') @@ -607,7 +642,8 @@ async def _handle_set_brightness(self, connection, udp_client, jsn, handle_respo if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') @@ -619,19 +655,21 @@ async def _handle_adjust_power_level(self, connection, udp_client, jsn, handle_r if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') async def _handle_set_power_level(self, connection, udp_client, jsn, handle_response, json_response, action): try: resp, value = await self.set_power_level(jsn, self.callbacks[action]) - response = json_response(action, resp, { "powerLevel": value}) + response = json_response(action, resp, {"powerLevel": value}) if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(str(e)) @@ -642,9 +680,10 @@ async def _handle_set_power_state(self, connection, udp_client, jsn, handle_resp if resp: await handle_response(response, connection, udp_client) except AssertionError: - self.logger.error("Signature verification failed for " + jsn.get('payload').get('action')) + self.logger.error( + "Signature verification failed for " + jsn.get('payload').get('action')) except Exception as e: self.logger.error(f'Error : {e}') - #else: - # self.logger.info(response_cmd + ' not found!') \ No newline at end of file + # else: + # self.logger.info(response_cmd + ' not found!') diff --git a/sinric/_events.py b/sinric/_events.py index c46c17a..55af495 100644 --- a/sinric/_events.py +++ b/sinric/_events.py @@ -5,22 +5,28 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ +from re import S from time import time, sleep +from typing import Final, Optional + +from loguru import Logger from ._queues import queue import uuid from ._signature import Signature from ._sinricpro_constants import SinricProConstants # noinspection PyBroadException + + class Events(Signature): - def __init__(self, connection, logger=None, secret_key=""): + def __init__(self, connection, logger: Optional[Logger] = None, secret_key: str = ""): self.connection = connection - self.logger = logger - self.secret_key = secret_key + self.logger: Optional[Logger] = logger + self.secret_key: Final[str] = secret_key Signature.__init__(self, self.secret_key) # noinspection PyBroadException - def raise_event(self, device_id, event_name, data=None): + def raise_event(self, device_id: str, event_name, data=None): if data is None: data = {} try: @@ -46,15 +52,18 @@ def json_response(action, device_id, value, type_of_interaction="PHYSICAL_INTERA return {"header": header, "payload": payload, "signature": signature} if event_name == SinricProConstants.SET_POWER_STATE: - response = json_response(event_name, device_id, {SinricProConstants.STATE: data.get("state", "Off")}) + response = json_response(event_name, device_id, { + SinricProConstants.STATE: data.get("state", "Off")}) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_POWER_LEVEL: - response = json_response(event_name, device_id, { SinricProConstants.POWER_LEVEL: data.get(SinricProConstants.POWER_LEVEL)}) + response = json_response(event_name, device_id, { + SinricProConstants.POWER_LEVEL: data.get(SinricProConstants.POWER_LEVEL)}) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_BRIGHTNESS: - response = json_response(event_name, device_id, { SinricProConstants.BRIGHTNESS : data.get(SinricProConstants.BRIGHTNESS) }) + response = json_response(event_name, device_id, { + SinricProConstants.BRIGHTNESS: data.get(SinricProConstants.BRIGHTNESS)}) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_COLOR: @@ -68,18 +77,20 @@ def json_response(action, device_id, value, type_of_interaction="PHYSICAL_INTERA queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_COLOR_TEMPERATURE: - response = json_response(event_name, device_id, { SinricProConstants.COLOR_TEMPERATURE: 2400 }) + response = json_response(event_name, device_id, { + SinricProConstants.COLOR_TEMPERATURE: 2400}) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.DOORBELLPRESS: - response = json_response(event_name, device_id, { "state": "pressed" }) + response = json_response( + event_name, device_id, {"state": "pressed"}) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.CURRENT_TEMPERATURE: response = json_response(event_name, device_id, { - SinricProConstants.TEMPERATURE : round(data.get(SinricProConstants.TEMPERATURE), 1), - SinricProConstants.HUMIDITY : round(data.get(SinricProConstants.HUMIDITY), 1), - }, type_of_interaction = "PERIODIC_POLL") + SinricProConstants.TEMPERATURE: round(data.get(SinricProConstants.TEMPERATURE), 1), + SinricProConstants.HUMIDITY: round(data.get(SinricProConstants.HUMIDITY), 1), + }, type_of_interaction="PERIODIC_POLL") queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.PUSH_NOTIFICATION: @@ -90,43 +101,50 @@ def json_response(action, device_id, value, type_of_interaction="PHYSICAL_INTERA elif event_name == SinricProConstants.SET_THERMOSTAT_MODE: response = json_response(event_name, device_id, { - SinricProConstants.THERMOSTATMODE: data.get(SinricProConstants.MODE) + SinricProConstants.THERMOSTATMODE: data.get( + SinricProConstants.MODE) }) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_RANGE_VALUE: response = json_response(event_name, device_id, { - SinricProConstants.RANGE_VALUE: data.get(SinricProConstants.RANGE_VALUE) + SinricProConstants.RANGE_VALUE: data.get( + SinricProConstants.RANGE_VALUE) }) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.MOTION: response = json_response(SinricProConstants.MOTION, device_id, { - SinricProConstants.STATE: data.get(SinricProConstants.STATE) + SinricProConstants.STATE: data.get( + SinricProConstants.STATE) }) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_CONTACT_STATE or event_name == SinricProConstants.SET_LOCK_STATE: response = json_response(event_name, device_id, { - SinricProConstants.STATE: data.get(SinricProConstants.STATE) + SinricProConstants.STATE: data.get( + SinricProConstants.STATE) }) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SET_VOLUME: response = json_response(event_name, device_id, { - SinricProConstants.VOLUME : data.get(SinricProConstants.VOLUME) + SinricProConstants.VOLUME: data.get( + SinricProConstants.VOLUME) }) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.SELECT_INPUT: response = json_response(event_name, device_id, { - SinricProConstants.INPUT: data.get(SinricProConstants.INPUT) + SinricProConstants.INPUT: data.get( + SinricProConstants.INPUT) }) queue.put([response, event_name, 'event']) elif event_name == SinricProConstants.MEDIA_CONTROL: response = json_response(event_name, device_id, { - SinricProConstants.CONTROL: data.get(SinricProConstants.CONTROL) + SinricProConstants.CONTROL: data.get( + SinricProConstants.CONTROL) }) queue.put([response, event_name, 'event']) @@ -175,7 +193,8 @@ def json_response(action, device_id, value, type_of_interaction="PHYSICAL_INTERA elif event_name == SinricProConstants.SET_MUTE: response = json_response(event_name, device_id, { - SinricProConstants.MUTE: data.get(SinricProConstants.MUTE, False) + SinricProConstants.MUTE: data.get( + SinricProConstants.MUTE, False) }) queue.put([response, event_name, 'event']) From 0c020b8f925561762b05bb4765b9b98695606581 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 6 Jan 2024 11:29:44 -0300 Subject: [PATCH 21/31] Typings for switch and multiswitch --- examples/multi_switch.py | 17 +++++++++++------ examples/switch.py | 15 ++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/multi_switch.py b/examples/multi_switch.py index 53e0378..faa7119 100644 --- a/examples/multi_switch.py +++ b/examples/multi_switch.py @@ -1,13 +1,18 @@ +from collections.abc import Callable +from typing import Final from sinric import SinricPro, SinricProConstants import asyncio -APP_KEY = '' -APP_SECRET = '' -SWITCH_ID_1 = '' -SWITCH_ID_2 = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +SWITCH_ID_1: Final[str] = '' +SWITCH_ID_2: Final[str] = '' +CallbackFunctions = Callable[[str, str], tuple[bool, str]] # Power state -def power_state(device_id, state): + +def power_state(device_id: str, state: str) -> tuple[bool, str]: + # TODO state should be enum if device_id == SWITCH_ID_1: print('device_id: {} state: {}'.format(device_id, state)) elif device_id == SWITCH_ID_2: @@ -18,7 +23,7 @@ def power_state(device_id, state): return True, state -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: power_state } diff --git a/examples/switch.py b/examples/switch.py index aa00ef1..3d402cc 100644 --- a/examples/switch.py +++ b/examples/switch.py @@ -1,17 +1,22 @@ +from collections.abc import Callable +from typing import Final from sinric import SinricPro, SinricProConstants import asyncio -APP_KEY = '' -APP_SECRET = '' -SWITCH_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +SWITCH_ID: Final[str] = '' +CallbackFunctions = Callable[[str, str], tuple[bool, str]] # Power state -def power_state(device_id, state): + +def power_state(device_id: str, state: str) -> tuple[bool, str]: + # TODO state should be enum print('device_id: {} state: {}'.format(device_id, state)) return True, state -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: power_state } From 640fe20186932fb6c6620d703fdf08fda644fd27 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 6 Jan 2024 11:31:09 -0300 Subject: [PATCH 22/31] Typings for push notifications --- examples/push_notification.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/push_notification.py b/examples/push_notification.py index d19dd15..1d2550a 100644 --- a/examples/push_notification.py +++ b/examples/push_notification.py @@ -1,10 +1,11 @@ +from typing import Final from sinric import SinricPro, SinricProConstants import asyncio from asyncio import sleep -APP_KEY = '' -APP_SECRET = '' -DEVICE_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +DEVICE_ID: Final[str] = '' async def events(): From 03ce8c5ea291a1282353619aa853e8ca3ecdd598 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 6 Jan 2024 11:32:27 -0300 Subject: [PATCH 23/31] Typings for temperature sensor --- examples/temperature_sensor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/temperature_sensor.py b/examples/temperature_sensor.py index c38a596..1ea1c8a 100644 --- a/examples/temperature_sensor.py +++ b/examples/temperature_sensor.py @@ -1,10 +1,11 @@ +from typing import Any, Final from sinric import SinricPro, SinricProConstants import asyncio from asyncio import sleep -APP_KEY = '' -APP_SECRET = '' -TEMPERATURE_SENSOR_ID = '' +APP_KEY: Final[str] = '' +APP_SECRET: Final[str] = '' +TEMPERATURE_SENSOR_ID: Final[str] = '' async def events(): @@ -17,7 +18,7 @@ async def events(): # Server will trottle / block IPs sending events too often. await sleep(60) -callbacks = {} +callbacks: dict[str, Any] = {} if __name__ == '__main__': loop = asyncio.get_event_loop() From b8880cbd7dc149b897b6d4de830750cf4ce750ea Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 18:42:02 -0300 Subject: [PATCH 24/31] Typings for speaker --- examples/speaker.py | 26 ++++++++++++++++++-------- sinric/_speaker_controller.py | 11 +++++++---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/examples/speaker.py b/examples/speaker.py index 5193a25..a6dc0ad 100644 --- a/examples/speaker.py +++ b/examples/speaker.py @@ -1,17 +1,23 @@ +import asyncio from collections.abc import Callable from numbers import Real from typing import Final, Union + from sinric import SinricPro, SinricProConstants, SinricProTypes -import asyncio APP_KEY: Final[str] = '' APP_SECRET: Final[str] = '' SPEAKER_ID: Final[str] = '' CallbackFunctions = Union[ - Callable[[str, str], tuple[bool, str]], # Power state + Callable[[str, str], tuple[bool, str]], # Power state / Set Mute + Callable[[str, Real], tuple[bool, Real]], # Set Volume Callable[[str, str, Real], tuple[bool, SinricProTypes.BandDictType]], # Set Bands + Callable[[str, str, Real, str], tuple[bool, + SinricProTypes.BandDictType]], # Adjust Bands + Callable[[str, SinricProTypes.BandDictType, + SinricProTypes.BandDictType, SinricProTypes.BandDictType], bool] # Reset Bands ] @@ -25,34 +31,38 @@ def set_bands(device_id: str, name: str, level: Real) -> tuple[bool, SinricProTy return True, {'name': name, 'level': level} -def adjust_bands(device_id, name, level, direction): +def adjust_bands(device_id: str, name: str, level: Real, direction: str) -> tuple[bool, SinricProTypes.BandDictType]: + # TODO direction should be an enum print('device_id: {}, name: {}, name: {}, direction: {}'.format( device_id, name, level, direction)) return True, {'name': name, 'level': level} -def reset_bands(device_id, band1, band2, band3): +def reset_bands(device_id: str, band1: SinricProTypes.BandDictType, + band2: SinricProTypes.BandDictType, band3: SinricProTypes.BandDictType) \ + -> bool: print('device_id: {}, band1: {}, band2: {}, band3: {}'.format( device_id, band1, band2, band3)) return True -def set_mode(device_id, mode): +def set_mode(device_id: str, mode: str) -> tuple[bool, str]: print('device_id: {} mode: {}'.format(device_id, mode)) return True, mode -def set_mute(device_id, mute): +def set_mute(device_id: str, mute: str) -> tuple[bool, str]: + # TODO: mute should be str? or a bool? or an enum? print('device_id: {} mute: {}'.format(device_id, mute)) return True, mute -def set_volume(device_id, volume): +def set_volume(device_id: str, volume: Real) -> tuple[bool, Real]: print('device_id: {} volume: {}'.format(device_id, volume)) return True, volume -callbacks = { +callbacks: dict[str, CallbackFunctions] = { SinricProConstants.SET_POWER_STATE: power_state, SinricProConstants.SET_BANDS: set_bands, SinricProConstants.SET_MODE: set_mode, diff --git a/sinric/_speaker_controller.py b/sinric/_speaker_controller.py index dda8a56..266c9a7 100644 --- a/sinric/_speaker_controller.py +++ b/sinric/_speaker_controller.py @@ -7,7 +7,7 @@ from collections.abc import Callable from numbers import Real -from typing import Union +from typing import Any, Union from sinric.helpers.set_limits import set_limits from ._sinricpro_constants import SinricProConstants from ._types import SinricProTypes @@ -18,13 +18,15 @@ class SpeakerController: def __init__(self): self.band: Real = 0 - async def set_bands(self, jsn, callback: Callable[[str, str, Real], tuple[bool, SinricProTypes.BandDictType]]) \ + async def set_bands(self, jsn: Any, callback: Callable[[str, str, Real], tuple[bool, SinricProTypes.BandDictType]]) \ -> tuple[bool, SinricProTypes.BandDictType]: value = jsn.get("payload").get(SinricProConstants.VALUE) bands = value.get(SinricProConstants.BANDS)[0] return callback(jsn.get("payload").get(SinricProConstants.DEVICEID), bands.get(SinricProConstants.NAME), bands.get(SinricProConstants.LEVEL)) - async def adjust_bands(self, jsn, callback): + async def adjust_bands(self, jsn: Any, callback: Callable[[str, str, Real, str], tuple[bool, + SinricProTypes.BandDictType]]) -> tuple[bool, + SinricProTypes.BandDictType]: value = jsn.get("payload").get(SinricProConstants.VALUE) bands = value.get(SinricProConstants.BANDS)[0] self.band += bands.get(SinricProConstants.LEVEL_DELTA, 0) @@ -32,7 +34,8 @@ async def adjust_bands(self, jsn, callback): return callback(jsn.get("payload").get(SinricProConstants.DEVICEID), bands.get(SinricProConstants.NAME), self.band, bands.get(SinricProConstants.LEVELDIRECTION)) - async def reset_bands(self, jsn, callback): + async def reset_bands(self, jsn: Any, callback: Callable[[str, SinricProTypes.BandDictType, + SinricProTypes.BandDictType, SinricProTypes.BandDictType], bool]) -> bool: value = jsn.get("payload").get(SinricProConstants.VALUE) band1 = value.get(SinricProConstants.BANDS)[0] band2 = value.get(SinricProConstants.BANDS)[1] From f0c7e4a3f5e72e4b79aa742a6b4f9b02f3d41c66 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 18:45:05 -0300 Subject: [PATCH 25/31] Typings for leaky bucket --- sinric/_leaky_bucket.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/sinric/_leaky_bucket.py b/sinric/_leaky_bucket.py index 092e2fa..5ee80e2 100644 --- a/sinric/_leaky_bucket.py +++ b/sinric/_leaky_bucket.py @@ -1,28 +1,31 @@ import time +from typing import Final + class LeakyBucket: - def __init__(self, bucket_size, drop_in_time, drop_out_time): - self.bucket_size = bucket_size - self.drop_in_time = drop_in_time - self.drop_out_time = drop_out_time - self.drop_in_bucket = 0 - self.last_drop = 0 + def __init__(self, bucket_size: int, drop_in_time: int, drop_out_time: int): + self.bucket_size: Final[int] = bucket_size + self.drop_in_time: int = drop_in_time + self.drop_out_time: int = drop_out_time + self.drop_in_bucket: int = 0 + self.last_drop: int = 0 - def millis(self): + def millis(self) -> int: return int(round(time.time() * 1000)) - def leak_drops(self): - actual_millis = self.millis() - drops_to_leak = round((actual_millis - self.last_drop) / self.drop_out_time) + def leak_drops(self) -> None: + actual_millis: int = self.millis() + drops_to_leak: int = round( + (actual_millis - self.last_drop) / self.drop_out_time) if drops_to_leak > 0: if self.drop_in_bucket <= drops_to_leak: self.drop_in_bucket = 0 else: self.drop_in_bucket = self.drop_in_bucket - drops_to_leak - def add_drop(self): + def add_drop(self) -> bool: self.leak_drops() - actual_millis = self.millis() + actual_millis: int = self.millis() if (self.drop_in_bucket < self.bucket_size) and ( (actual_millis - self.last_drop) > (self.drop_in_bucket + self.drop_in_time)): self.drop_in_bucket = self.drop_in_bucket + 1 @@ -30,4 +33,4 @@ def add_drop(self): return True if self.drop_in_bucket >= self.bucket_size: return False - return False \ No newline at end of file + return False From ea99eb253fc1e65e97cad88120981b4a641358c5 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 18:48:56 -0300 Subject: [PATCH 26/31] Typings for signature --- .vscode/settings.json | 4 ++++ sinric/_signature.py | 17 ++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3d6dde6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.analysis.typeCheckingMode": "off", + "python.analysis.autoImportCompletions": true +} diff --git a/sinric/_signature.py b/sinric/_signature.py index 45eec76..b78594c 100644 --- a/sinric/_signature.py +++ b/sinric/_signature.py @@ -2,22 +2,25 @@ from base64 import b64encode import hmac as sinricHmac from hashlib import sha256 +from typing import Final, Optional class Signature: - def __init__(self, secret_key): - self.secret_key = secret_key + def __init__(self, secret_key: str): + self.secret_key: Final[str] = secret_key + self.hmac: Optional[sinricHmac.HMAC] = None - def verify_signature(self, payload, signature) -> bool: + def verify_signature(self, payload, signature: str) -> bool: self.hmac = sinricHmac.new(self.secret_key.encode('utf-8'), dumps(payload, separators=(',', ':')).encode('utf-8'), sha256) return b64encode(self.hmac.digest()).decode('utf-8') == signature - def get_signature(self, payload) -> dict: - reply_hmac = sinricHmac.new(self.secret_key.encode('utf-8'), - dumps(payload, separators=(',', ':')).encode('utf-8'), sha256) + def get_signature(self, payload) -> dict[str, str]: + # TODO is there a special type for json payload? + reply_hmac: sinricHmac.HMAC = sinricHmac.new(self.secret_key.encode('utf-8'), + dumps(payload, separators=(',', ':')).encode('utf-8'), sha256) - encoded_hmac = b64encode(reply_hmac.digest()) + encoded_hmac: bytes = b64encode(reply_hmac.digest()) return { "HMAC": encoded_hmac.decode('utf-8') From 14c59a368d1168bf67c06747122cc0052ca417a5 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 18:50:48 -0300 Subject: [PATCH 27/31] Bugfix --- sinric/_signature.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sinric/_signature.py b/sinric/_signature.py index b78594c..72c27c2 100644 --- a/sinric/_signature.py +++ b/sinric/_signature.py @@ -7,7 +7,7 @@ class Signature: def __init__(self, secret_key: str): - self.secret_key: Final[str] = secret_key + self.secret_key: str = secret_key self.hmac: Optional[sinricHmac.HMAC] = None def verify_signature(self, payload, signature: str) -> bool: From f2d2de8a24d9b0507ed30425163a94b82b4c41b7 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 18:54:54 -0300 Subject: [PATCH 28/31] Cleanup --- sinric/_sinricpro.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sinric/_sinricpro.py b/sinric/_sinricpro.py index 88fa9aa..0115bd7 100644 --- a/sinric/_sinricpro.py +++ b/sinric/_sinricpro.py @@ -14,18 +14,9 @@ from ._events import Events from ._types import SinricProTypes from typing import ( - Any, - Awaitable, - Callable, Final, - Iterable as TypingIterable, - List, NoReturn, Optional, - Set, - Type, - Union, - cast, ) # logger.add("{}.log".format("sinricpro_logfile"), rotation="10 MB") From 7b1898cd69e8cbd511ae20d1960999dafb806106 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 19:11:00 -0300 Subject: [PATCH 29/31] Added connection --- sinric/_sinricpro_websocket.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sinric/_sinricpro_websocket.py b/sinric/_sinricpro_websocket.py index 6e51169..9e20a1c 100644 --- a/sinric/_sinricpro_websocket.py +++ b/sinric/_sinricpro_websocket.py @@ -8,7 +8,7 @@ import asyncio import json from time import time -from typing import Final, NoReturn +from typing import Final, NoReturn, Optional from loguru import Logger import pkg_resources @@ -32,7 +32,7 @@ def __init__(self, app_key: str, device_id: str, callbacks: SinricProTypes.Reque self.restore_states: Final[bool] = restore_states self.logger: Final[Logger] = logger self.device_ids: Final[list[str]] = device_id - self.connection = None + self.connection: Optional[client.Connect] = None self.callbacks: SinricProTypes.RequestCallbacks = callbacks self.loop_delay: Final[float] = loop_delay @@ -41,16 +41,16 @@ def __init__(self, app_key: str, device_id: str, callbacks: SinricProTypes.Reque self.enableTrace: Final[bool] = enable_trace Signature.__init__(self, self.secret_key) - async def connect(self): # Producer + async def connect(self) -> client.Connect: # Producer sdk_version = pkg_resources.require("sinricpro")[0].version - self.connection = await client.connect('wss://ws.sinric.pro', - extra_headers={'appkey': self.app_key, - 'deviceids': ';'.join(self.device_ids), - 'platform': 'python', - 'sdkversion': sdk_version, - 'restoredevicestates': ( - 'true' if self.restore_states else 'false')}, - ping_interval=30000, ping_timeout=10000) + self.connection: client.Connect = await client.connect('wss://ws.sinric.pro', + extra_headers={'appkey': self.app_key, + 'deviceids': ';'.join(self.device_ids), + 'platform': 'python', + 'sdkversion': sdk_version, + 'restoredevicestates': ( + 'true' if self.restore_states else 'false')}, + ping_interval=30000, ping_timeout=10000) if self.connection.open: self.logger.success("Connected :)") timestamp = await self.connection.recv() @@ -59,7 +59,7 @@ async def connect(self): # Producer 'Timestamp is not in sync. Please check your system time.') return self.connection - async def receive_message(self, connection) -> NoReturn: + async def receive_message(self, connection: client.Connect) -> NoReturn: while True: try: message = await waitAsync(connection.recv()) From 4c8ef4b8a5d0dfb20865e74f27c1f0c083864bb5 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sat, 13 Jan 2024 19:11:13 -0300 Subject: [PATCH 30/31] Began typing in callback handler --- sinric/_callback_handler.py | 54 ++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/sinric/_callback_handler.py b/sinric/_callback_handler.py index ee1ade5..e689e7b 100644 --- a/sinric/_callback_handler.py +++ b/sinric/_callback_handler.py @@ -5,27 +5,33 @@ * This file is part of the Sinric Pro (https://github.com/sinricpro/) """ -from re import X +from collections.abc import Callable +import hmac as sinricHmac from json import dumps +from re import X from time import time +from typing import Any, Final, NoReturn, Optional from uuid import uuid4 -from ._power_controller import PowerController +import loguru +from websockets import client + from ._brightness_controller import BrightnessController -from ._power_level_controller import PowerLevelController +from ._camera_stream_controller import CameraStreamController from ._color_controller import ColorController from ._color_temperature import ColorTemperatureController -from ._thermostat_controller import ThermostateMode -from ._range_value_controller import RangeValueController -from ._temperature_controller import TemperatureController -from ._tv_contorller import TvController -from ._speaker_controller import SpeakerController -from ._mode_controller import ModeController -from ._camera_stream_controller import CameraStreamController +from ._leaky_bucket import LeakyBucket from ._lock_controller import LockStateController +from ._mode_controller import ModeController +from ._power_controller import PowerController +from ._power_level_controller import PowerLevelController +from ._range_value_controller import RangeValueController from ._signature import Signature -from ._leaky_bucket import LeakyBucket from ._sinricpro_constants import SinricProConstants +from ._speaker_controller import SpeakerController +from ._temperature_controller import TemperatureController +from ._thermostat_controller import ThermostateMode +from ._tv_contorller import TvController from ._types import SinricProTypes # noinspection PyBroadException @@ -34,11 +40,11 @@ class CallBackHandler(PowerLevelController, PowerController, BrightnessController, ColorController, ColorTemperatureController, ThermostateMode, RangeValueController, TemperatureController, TvController, SpeakerController, LockStateController, ModeController, CameraStreamController, Signature): - def __init__(self, callbacks: SinricProTypes.RequestCallbacks, trace_bool: bool, logger, enable_track=False, secret_key=""): - self.myHmac = None - self.secret_key = secret_key - self.bucket = LeakyBucket(10, 1000, 60000) - self.enable_track = enable_track + def __init__(self, callbacks: SinricProTypes.RequestCallbacks, trace_bool: bool, logger: loguru.Logger, enable_track: bool = False, secret_key: str = ""): + self.myHmac: Optional[sinricHmac.HMAC] = None + self.secret_key: Final[str] = secret_key + self.bucket: Final[LeakyBucket] = LeakyBucket(10, 1000, 60000) + self.enable_track: bool = enable_track PowerLevelController.__init__(self, 0) BrightnessController.__init__(self, 0) @@ -56,16 +62,19 @@ def __init__(self, callbacks: SinricProTypes.RequestCallbacks, trace_bool: bool, self, 0, [2200, 2700, 4000, 5500, 7000]) CameraStreamController.__init__(self) - self.callbacks = callbacks - self.logger = logger - self.trace_response = trace_bool + self.callbacks: SinricProTypes.RequestCallbacks = callbacks + self.logger: loguru.Logger = logger + self.trace_response: bool = trace_bool - async def handle_callbacks(self, data_array, connection, udp_client): + async def handle_callbacks(self, data_array: list[Any], connection: client.Connect, udp_client): jsn = data_array[0] response_cmd = data_array[1] message_type = data_array[2] + # TODO check this in python39 + handleResponseType: Callable[[dict, client.Connect, Any], NoReturn] - async def handle_response(response, connection, udp_client): + async def handle_response(response: dict, connection: client.Connect, udp_client) -> NoReturn: + # TODO What are types for connection and udp clinet? if self.trace_response: self.logger.info(f"Response : {dumps(response)}") if response_cmd == 'socket_response': @@ -74,7 +83,8 @@ async def handle_response(response, connection, udp_client): udp_client.sendResponse( dumps(response).encode('ascii'), data_array[2]) - def json_response(action, resp, data_dict, instance_id='') -> dict: + def json_response(action: SinricProConstants, resp: bool, data_dict: dict[str, Any], instance_id='') -> dict: + # TODO Define a json response type header = { "payloadVersion": 2, "signatureVersion": 1 From fee33c369d85f52057504f63d5cbf04f8d237140 Mon Sep 17 00:00:00 2001 From: cojmeister Date: Sun, 14 Jan 2024 08:36:47 +0100 Subject: [PATCH 31/31] Removed .vscode --- .vscode/settings.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3d6dde6..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "python.analysis.typeCheckingMode": "off", - "python.analysis.autoImportCompletions": true -}