python-swidget 1.2.2__py3-none-any.whl → 1.2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_swidget-1.2.2.dist-info → python_swidget-1.2.4.dist-info}/METADATA +3 -2
- python_swidget-1.2.4.dist-info/RECORD +16 -0
- swidget/__init__.py +10 -5
- swidget/cli.py +55 -43
- swidget/discovery.py +35 -8
- swidget/provision.py +57 -37
- swidget/swidgetdevice.py +302 -95
- swidget/swidgetdimmer.py +37 -14
- swidget/swidgetoutlet.py +19 -7
- swidget/swidgetswitch.py +19 -7
- swidget/swidgettimerswitch.py +23 -7
- swidget/websocket.py +39 -18
- python_swidget-1.2.2.dist-info/RECORD +0 -16
- {python_swidget-1.2.2.dist-info → python_swidget-1.2.4.dist-info}/WHEEL +0 -0
- {python_swidget-1.2.2.dist-info → python_swidget-1.2.4.dist-info}/entry_points.txt +0 -0
swidget/swidgetdimmer.py
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
+
"""Module for SwidgetDimmer."""
|
|
1
2
|
import logging
|
|
2
3
|
|
|
3
|
-
from swidget.swidgetdevice import (
|
|
4
|
-
DeviceType,
|
|
5
|
-
SwidgetDevice
|
|
6
|
-
)
|
|
7
4
|
from swidget.exceptions import SwidgetException
|
|
5
|
+
from swidget.swidgetdevice import DeviceType, SwidgetDevice
|
|
8
6
|
|
|
9
7
|
_LOGGER = logging.getLogger(__name__)
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
class SwidgetDimmer(SwidgetDevice):
|
|
11
|
+
"""Representation of a Swidget Dimmer device."""
|
|
13
12
|
|
|
14
|
-
def __init__(
|
|
15
|
-
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
host,
|
|
16
|
+
token_name: str,
|
|
17
|
+
secret_key: str,
|
|
18
|
+
use_https: bool,
|
|
19
|
+
use_websockets: bool,
|
|
20
|
+
) -> None:
|
|
21
|
+
super().__init__(
|
|
22
|
+
host=host,
|
|
23
|
+
token_name=token_name,
|
|
24
|
+
secret_key=secret_key,
|
|
25
|
+
use_https=use_https,
|
|
26
|
+
use_websockets=use_websockets,
|
|
27
|
+
)
|
|
16
28
|
self._device_type = DeviceType.Dimmer
|
|
17
29
|
|
|
18
30
|
@property # type: ignore
|
|
@@ -25,26 +37,37 @@ class SwidgetDimmer(SwidgetDevice):
|
|
|
25
37
|
if not self.is_dimmable:
|
|
26
38
|
raise SwidgetException("Device is not dimmable.")
|
|
27
39
|
try:
|
|
28
|
-
return self.assemblies[
|
|
40
|
+
return self.assemblies["host"].components["0"].functions["level"]["now"]
|
|
29
41
|
except KeyError:
|
|
30
|
-
return self.assemblies[
|
|
42
|
+
return self.assemblies["host"].components["0"].functions["level"]["default"]
|
|
31
43
|
|
|
32
44
|
async def set_brightness(self, brightness) -> None:
|
|
33
45
|
"""Set the brightness of the device."""
|
|
34
|
-
_LOGGER.debug(
|
|
35
|
-
|
|
46
|
+
_LOGGER.debug(
|
|
47
|
+
"SwidgetDimmer.set_brightness() called with brightness: {brightness}"
|
|
48
|
+
)
|
|
49
|
+
self.assemblies["host"].components["0"].functions["level"]["now"] = brightness
|
|
36
50
|
await self.send_command(
|
|
37
|
-
assembly="host",
|
|
51
|
+
assembly="host",
|
|
52
|
+
component="0",
|
|
53
|
+
function="level",
|
|
54
|
+
command={"now": brightness},
|
|
38
55
|
)
|
|
39
56
|
|
|
40
57
|
async def set_default_brightness(self, brightness) -> None:
|
|
41
|
-
|
|
58
|
+
"""Set the brightness of the device when it is turned on."""
|
|
59
|
+
_LOGGER.debug(
|
|
60
|
+
"SwidgetDimmer.set_default_brightness() called with brightness: {brightness}"
|
|
61
|
+
)
|
|
42
62
|
await self.send_command(
|
|
43
|
-
assembly="host",
|
|
63
|
+
assembly="host",
|
|
64
|
+
component="0",
|
|
65
|
+
function="level",
|
|
66
|
+
command={"default": brightness},
|
|
44
67
|
)
|
|
45
68
|
|
|
46
69
|
@property # type: ignore
|
|
47
70
|
def is_dimmable(self) -> bool:
|
|
48
71
|
"""Whether the switch supports brightness changes."""
|
|
49
72
|
_LOGGER.debug("SwidgetDimmer.is_dimmable() called")
|
|
50
|
-
return True
|
|
73
|
+
return True
|
swidget/swidgetoutlet.py
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
SwidgetDevice
|
|
4
|
-
)
|
|
1
|
+
"""Module for SwidgetOutlet."""
|
|
2
|
+
from swidget.swidgetdevice import DeviceType, SwidgetDevice
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
class SwidgetOutlet(SwidgetDevice):
|
|
6
|
+
"""Representation of a Swidget Outlet device."""
|
|
8
7
|
|
|
9
|
-
def __init__(
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
host,
|
|
11
|
+
token_name: str,
|
|
12
|
+
secret_key: str,
|
|
13
|
+
use_https: bool,
|
|
14
|
+
use_websockets: bool,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(
|
|
17
|
+
host=host,
|
|
18
|
+
token_name=token_name,
|
|
19
|
+
secret_key=secret_key,
|
|
20
|
+
use_https=use_https,
|
|
21
|
+
use_websockets=use_websockets,
|
|
22
|
+
)
|
|
23
|
+
self._device_type = DeviceType.Outlet
|
swidget/swidgetswitch.py
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
SwidgetDevice
|
|
4
|
-
)
|
|
1
|
+
"""Module for a generic SwidgetSwitch."""
|
|
2
|
+
from swidget.swidgetdevice import DeviceType, SwidgetDevice
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
class SwidgetSwitch(SwidgetDevice):
|
|
6
|
+
"""Representation of a Swidget Switch device."""
|
|
8
7
|
|
|
9
|
-
def __init__(
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
host,
|
|
11
|
+
token_name: str,
|
|
12
|
+
secret_key: str,
|
|
13
|
+
use_https: bool,
|
|
14
|
+
use_websockets: bool,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(
|
|
17
|
+
host=host,
|
|
18
|
+
token_name=token_name,
|
|
19
|
+
secret_key=secret_key,
|
|
20
|
+
use_https=use_https,
|
|
21
|
+
use_websockets=use_websockets,
|
|
22
|
+
)
|
|
23
|
+
self._device_type = DeviceType.Switch
|
swidget/swidgettimerswitch.py
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
|
+
"""Module for SwidgetTimerSwitch."""
|
|
1
2
|
import logging
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
|
-
from swidget.swidgetdevice import
|
|
5
|
-
DeviceType,
|
|
6
|
-
)
|
|
5
|
+
from swidget.swidgetdevice import DeviceType
|
|
7
6
|
from swidget.swidgetswitch import SwidgetSwitch
|
|
8
7
|
|
|
9
8
|
_LOGGER = logging.getLogger(__name__)
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class SwidgetTimerSwitch(SwidgetSwitch):
|
|
12
|
+
"""Class to represent a Swidget Timer Switch (also known as a 20/40/60 switch)."""
|
|
13
13
|
|
|
14
|
-
def __init__(
|
|
15
|
-
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
host,
|
|
17
|
+
token_name: str,
|
|
18
|
+
secret_key: str,
|
|
19
|
+
use_https: bool,
|
|
20
|
+
use_websockets: bool,
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__(
|
|
23
|
+
host=host,
|
|
24
|
+
token_name=token_name,
|
|
25
|
+
secret_key=secret_key,
|
|
26
|
+
use_https=use_https,
|
|
27
|
+
use_websockets=use_websockets,
|
|
28
|
+
)
|
|
16
29
|
self._device_type = DeviceType.TimerSwitch
|
|
17
30
|
|
|
18
31
|
async def set_countdown_timer(self, minutes) -> Any:
|
|
19
32
|
"""Set the countdown timer."""
|
|
20
33
|
_LOGGER.debug("SwidgetTimerSwitch.set_brightness() called")
|
|
21
34
|
await self.send_command(
|
|
22
|
-
assembly="host",
|
|
23
|
-
|
|
35
|
+
assembly="host",
|
|
36
|
+
component="0",
|
|
37
|
+
function="timer",
|
|
38
|
+
command={"duration": minutes},
|
|
39
|
+
)
|
swidget/websocket.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
"""Module to handle websocket connections to Swidget devices."""
|
|
1
2
|
import asyncio
|
|
2
|
-
import aiohttp
|
|
3
|
-
from aiohttp import ClientWebSocketResponse, WSMsgType
|
|
4
3
|
import logging
|
|
5
4
|
import socket
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
7
|
+
import aiohttp
|
|
8
|
+
from aiohttp import ClientWebSocketResponse, WSMsgType
|
|
9
|
+
|
|
8
10
|
_LOGGER = logging.getLogger(__name__)
|
|
9
11
|
|
|
10
12
|
|
|
@@ -20,7 +22,7 @@ async def cancel_task(*tasks: asyncio.Task | None) -> None:
|
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class SwidgetWebsocket:
|
|
23
|
-
"""A websocket connection to a Swidget Device"""
|
|
25
|
+
"""A websocket connection to a Swidget Device."""
|
|
24
26
|
|
|
25
27
|
# pylint: disable=too-many-instance-attributes
|
|
26
28
|
_client: aiohttp.ClientWebSocketResponse | None = None
|
|
@@ -43,11 +45,12 @@ class SwidgetWebsocket:
|
|
|
43
45
|
self._state = None
|
|
44
46
|
self.failed_attempts = 0
|
|
45
47
|
self._error_reason = None
|
|
46
|
-
self.headers = {
|
|
48
|
+
self.headers = {"Connection": "Upgrade"}
|
|
47
49
|
self._receiver_task: asyncio.Task | None = None
|
|
48
50
|
|
|
49
51
|
@property
|
|
50
52
|
def connected(self) -> bool:
|
|
53
|
+
"""Return of status of whether the device is currently connected."""
|
|
51
54
|
return self._client is not None and not self._client.closed
|
|
52
55
|
|
|
53
56
|
@property
|
|
@@ -56,15 +59,15 @@ class SwidgetWebsocket:
|
|
|
56
59
|
return self._client
|
|
57
60
|
|
|
58
61
|
def get_uri(self, host, token_name, secret_key):
|
|
59
|
-
"""Generate the websocket URI"""
|
|
62
|
+
"""Generate the websocket URI."""
|
|
60
63
|
if self.use_security:
|
|
61
64
|
return f"wss://{host}/api/v1/sock?{token_name}={secret_key}"
|
|
62
65
|
else:
|
|
63
66
|
return f"ws://{host}/api/v1/sock?{token_name}={secret_key}"
|
|
64
67
|
|
|
65
68
|
async def connect(self) -> None:
|
|
66
|
-
_LOGGER.debug("websocket.connect() called")
|
|
67
69
|
"""Create a new connection and, optionally, start the monitor."""
|
|
70
|
+
_LOGGER.debug("websocket.connect() called")
|
|
68
71
|
await cancel_task(self._receiver_task)
|
|
69
72
|
if self.connected:
|
|
70
73
|
_LOGGER.debug("Websocket already connected")
|
|
@@ -78,13 +81,22 @@ class SwidgetWebsocket:
|
|
|
78
81
|
|
|
79
82
|
try:
|
|
80
83
|
_LOGGER.debug("Trying to connect")
|
|
81
|
-
self._client = await self.session.ws_connect(
|
|
84
|
+
self._client = await self.session.ws_connect(
|
|
85
|
+
url=self.uri,
|
|
86
|
+
headers=self.headers,
|
|
87
|
+
verify_ssl=self._verify_ssl,
|
|
88
|
+
heartbeat=30,
|
|
89
|
+
)
|
|
82
90
|
_LOGGER.debug("Websocket now connected")
|
|
83
91
|
except aiohttp.WSServerHandshakeError as handshake_error:
|
|
84
|
-
_LOGGER.error(
|
|
92
|
+
_LOGGER.error(
|
|
93
|
+
f"Error occurred during websocket handshake: {handshake_error}"
|
|
94
|
+
)
|
|
85
95
|
raise
|
|
86
96
|
except aiohttp.ClientConnectionError as connection_error:
|
|
87
|
-
_LOGGER.error(
|
|
97
|
+
_LOGGER.error(
|
|
98
|
+
f"Error connecting to the websocket server: {connection_error}"
|
|
99
|
+
)
|
|
88
100
|
raise
|
|
89
101
|
except socket.gaierror as gai_error:
|
|
90
102
|
_LOGGER.error(f"Error resolving host: {gai_error}")
|
|
@@ -95,12 +107,14 @@ class SwidgetWebsocket:
|
|
|
95
107
|
self._receiver_task = asyncio.ensure_future(self.listen())
|
|
96
108
|
|
|
97
109
|
async def close(self) -> None:
|
|
110
|
+
"""Close the websocket."""
|
|
98
111
|
_LOGGER.debug("websocket.close() called")
|
|
99
112
|
if self._client is not None and not self._client.closed:
|
|
100
113
|
await self._client.close()
|
|
101
114
|
self._client = None
|
|
102
115
|
|
|
103
116
|
async def disconnect(self) -> None:
|
|
117
|
+
"""Wrapper of the close() function."""
|
|
104
118
|
await self.close()
|
|
105
119
|
|
|
106
120
|
async def send_str(self, message):
|
|
@@ -110,18 +124,24 @@ class SwidgetWebsocket:
|
|
|
110
124
|
raise ConnectionError
|
|
111
125
|
message = str(message)
|
|
112
126
|
_LOGGER.debug(f"Sending messsage over websocket: {message}")
|
|
113
|
-
await self._client.send_str(f
|
|
127
|
+
await self._client.send_str(f"{message}")
|
|
114
128
|
|
|
115
129
|
async def listen(self):
|
|
130
|
+
"""Ask the client to listen to websocket events."""
|
|
116
131
|
_LOGGER.debug("websocket.listen() called")
|
|
132
|
+
# Check if the websocket is connected
|
|
117
133
|
if not self._client or not self.connected:
|
|
118
|
-
|
|
134
|
+
_LOGGER.error("Websocket is not connected")
|
|
135
|
+
raise ConnectionError("Websocket is not connected")
|
|
119
136
|
|
|
120
137
|
while not self._client.closed:
|
|
121
|
-
|
|
138
|
+
try:
|
|
139
|
+
message = await self._client.receive()
|
|
140
|
+
except StopAsyncIteration:
|
|
141
|
+
break
|
|
122
142
|
|
|
123
143
|
if message.type == aiohttp.WSMsgType.ERROR:
|
|
124
|
-
raise
|
|
144
|
+
raise ConnectionError("Websocket error occurred")
|
|
125
145
|
|
|
126
146
|
if message.type == aiohttp.WSMsgType.TEXT:
|
|
127
147
|
message_data = message.json()
|
|
@@ -133,7 +153,10 @@ class SwidgetWebsocket:
|
|
|
133
153
|
aiohttp.WSMsgType.CLOSED,
|
|
134
154
|
aiohttp.WSMsgType.CLOSING,
|
|
135
155
|
):
|
|
136
|
-
_LOGGER.error("Connection to the Swidget WebSocket
|
|
156
|
+
_LOGGER.error("Connection to the Swidget WebSocket has been closed")
|
|
157
|
+
break
|
|
158
|
+
|
|
159
|
+
self._client = None # Ensure client is set to None when closed
|
|
137
160
|
|
|
138
161
|
async def receive_message_or_raise(self) -> Any:
|
|
139
162
|
"""Receive ONE (raw) message or raise."""
|
|
@@ -147,9 +170,7 @@ class SwidgetWebsocket:
|
|
|
147
170
|
raise ConnectionError
|
|
148
171
|
|
|
149
172
|
if ws_msg.type != WSMsgType.TEXT:
|
|
150
|
-
raise ValueError(
|
|
151
|
-
f"Received non-Text message: {ws_msg.type}: {ws_msg.data}"
|
|
152
|
-
)
|
|
173
|
+
raise ValueError(f"Received non-Text message: {ws_msg.type}: {ws_msg.data}")
|
|
153
174
|
|
|
154
175
|
try:
|
|
155
176
|
msg = ws_msg.json()
|
|
@@ -164,4 +185,4 @@ class SwidgetWebsocket:
|
|
|
164
185
|
def __repr__(self) -> str:
|
|
165
186
|
"""Return the representation."""
|
|
166
187
|
prefix = "" if self.connected else "not "
|
|
167
|
-
return f"{type(self).__name__}(ws_server_url={self.host}, {prefix}connected)"
|
|
188
|
+
return f"{type(self).__name__}(ws_server_url={self.host}, {prefix}connected)"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
swidget/__init__.py,sha256=sUaN7mSvEe40HDHjtEiqUtUGdb3239XbFLb14PnOpHw,1330
|
|
2
|
-
swidget/cli.py,sha256=G61GQijygVL1W59EQVVk3dA7SOikWhZsZ5iy33cB5FQ,8951
|
|
3
|
-
swidget/discovery.py,sha256=b5-C75d0qNl3-PGK6vq5i02uTFaRlQq_yc6l_qG4FmM,3865
|
|
4
|
-
swidget/exceptions.py,sha256=JOSwaOwwv8YLry687vZmTQLY-BrrTpCs0CIZr7u-J48,79
|
|
5
|
-
swidget/provision.py,sha256=Mw_0lHUj584F6fg2H80WVOd3Iz57C300hDnNDi1TSIw,4591
|
|
6
|
-
swidget/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
swidget/swidgetdevice.py,sha256=HdRyiQ3S0X8Dxq3p07Ksbbk5Xer39pVWPtPSPnf9ex8,21853
|
|
8
|
-
swidget/swidgetdimmer.py,sha256=doPajuRoSSI0aU3WlT-t_WZyxsRyxAB-uMiXYz-YP88,1969
|
|
9
|
-
swidget/swidgetoutlet.py,sha256=XUz42SV3_CaNp0gOGtBa1rAqG5xpmGpdLMem-OHCMM4,402
|
|
10
|
-
swidget/swidgetswitch.py,sha256=VorjxnV42wWmu77CmWaasrMOAFIO15tQReRyaN7D-qU,402
|
|
11
|
-
swidget/swidgettimerswitch.py,sha256=7-F81U7VWi-YCW6vYyOcZyKjttHCuW_P5LLbtLmdzfk,820
|
|
12
|
-
swidget/websocket.py,sha256=27wCxJNmOAK1ba8lLQ3kxXEgg6Cp30Uri6P5OpaWV00,5768
|
|
13
|
-
python_swidget-1.2.2.dist-info/METADATA,sha256=vbkdFQ4SWOjMgYuZi3Gne1uiiezeuNg55hjc-Q1379Q,1853
|
|
14
|
-
python_swidget-1.2.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
15
|
-
python_swidget-1.2.2.dist-info/entry_points.txt,sha256=ksGZuoFNqyktLnxPUCFDHokC_7n1uu4GSMUEerwwDmY,43
|
|
16
|
-
python_swidget-1.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|