python-swidget 1.0.1__tar.gz → 1.0.2__tar.gz
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.0.1 → python_swidget-1.0.2}/PKG-INFO +3 -1
- {python_swidget-1.0.1 → python_swidget-1.0.2}/pyproject.toml +3 -1
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/cli.py +9 -17
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/discovery.py +10 -11
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/swidgetdevice.py +48 -49
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/swidgetdimmer.py +2 -2
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/swidgetswitch.py +1 -5
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/swidgettimerswitch.py +3 -1
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/websocket.py +14 -12
- {python_swidget-1.0.1 → python_swidget-1.0.2}/README.md +0 -0
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/__init__.py +0 -0
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/exceptions.py +0 -0
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/provision.py +0 -0
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/py.typed +0 -0
- {python_swidget-1.0.1 → python_swidget-1.0.2}/swidget/swidgetoutlet.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-swidget
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Python API for Swidget smart devices
|
|
5
5
|
Home-page: https://github.com/swidget/python-swidget
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -21,10 +21,12 @@ Requires-Dist: importlib-metadata
|
|
|
21
21
|
Requires-Dist: m2r (>=0,<1) ; extra == "docs"
|
|
22
22
|
Requires-Dist: mistune (<2.0.0) ; extra == "docs"
|
|
23
23
|
Requires-Dist: pydantic (>=1,<2)
|
|
24
|
+
Requires-Dist: requests (==2.31.0)
|
|
24
25
|
Requires-Dist: sphinx (>=4,<5) ; extra == "docs"
|
|
25
26
|
Requires-Dist: sphinx_rtd_theme (>=0,<1) ; extra == "docs"
|
|
26
27
|
Requires-Dist: sphinxcontrib-programoutput (>=0,<1) ; extra == "docs"
|
|
27
28
|
Requires-Dist: ssdp (==1.1.1)
|
|
29
|
+
Requires-Dist: types-requests (==2.31.0.20240406)
|
|
28
30
|
Project-URL: Repository, https://github.com/swidget/python-swidget
|
|
29
31
|
Description-Content-Type: text/markdown
|
|
30
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-swidget"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.2"
|
|
4
4
|
description = "Python API for Swidget smart devices"
|
|
5
5
|
license = "GPL-3.0-or-later"
|
|
6
6
|
authors = ["Swidget"]
|
|
@@ -22,6 +22,8 @@ importlib-metadata = "*"
|
|
|
22
22
|
asyncclick = ">=8"
|
|
23
23
|
pydantic = "^1"
|
|
24
24
|
ssdp = "1.1.1"
|
|
25
|
+
requests = "2.31.0"
|
|
26
|
+
types-requests = "2.31.0.20240406"
|
|
25
27
|
|
|
26
28
|
# required only for docs
|
|
27
29
|
sphinx = { version = "^4", optional = true }
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""python-swidget cli tool."""
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
|
+
from typing import Any, cast
|
|
4
5
|
from pprint import pformat as pf
|
|
5
|
-
from typing import cast
|
|
6
6
|
|
|
7
7
|
import asyncclick as click
|
|
8
8
|
from contextlib import asynccontextmanager
|
|
@@ -25,8 +25,6 @@ TYPE_TO_CLASS = {
|
|
|
25
25
|
"pana_switch": SwidgetTimerSwitch
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
click.anyio_backend = "asyncio"
|
|
29
|
-
|
|
30
28
|
|
|
31
29
|
pass_dev = click.make_pass_decorator(SwidgetDevice)
|
|
32
30
|
|
|
@@ -72,7 +70,7 @@ async def cli(ctx, host, password, debug, http_only, type):
|
|
|
72
70
|
if ctx.invoked_subcommand == "discover" or ctx.invoked_subcommand == "wifi":
|
|
73
71
|
return
|
|
74
72
|
if host is None:
|
|
75
|
-
click.echo("No
|
|
73
|
+
click.echo("No hostname or IP given, trying discovery..")
|
|
76
74
|
await ctx.invoke(discover)
|
|
77
75
|
return
|
|
78
76
|
if type is not None:
|
|
@@ -193,31 +191,25 @@ async def state(dev: SwidgetDevice):
|
|
|
193
191
|
@click.argument("command")
|
|
194
192
|
async def raw_command(dev: SwidgetDevice, assembly, component, function, command):
|
|
195
193
|
"""Run a raw command on the device."""
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if parameters is not None:
|
|
199
|
-
parameters = ast.literal_eval(parameters)
|
|
200
|
-
|
|
201
|
-
res = await dev.send_command(assembly, component, function, command)
|
|
202
|
-
|
|
203
|
-
click.echo(res)
|
|
204
|
-
return res
|
|
194
|
+
await dev.send_command(assembly, component, function, command)
|
|
195
|
+
click.echo("Command sent")
|
|
205
196
|
|
|
206
197
|
|
|
207
198
|
@cli.command()
|
|
208
199
|
@click.argument("brightness", type=click.IntRange(0, 100), default=None, required=False)
|
|
209
200
|
@pass_dev
|
|
210
|
-
async def brightness(dev:
|
|
201
|
+
async def brightness(dev: SwidgetDevice, brightness: Any=None):
|
|
202
|
+
dimmer_dev = cast(SwidgetDimmer, dev)
|
|
211
203
|
"""Get or set brightness."""
|
|
212
|
-
if not
|
|
204
|
+
if not dimmer_dev.is_dimmer:
|
|
213
205
|
click.echo("This device does not support brightness.")
|
|
214
206
|
return
|
|
215
207
|
|
|
216
208
|
if brightness is None:
|
|
217
|
-
click.echo(f"Brightness: {
|
|
209
|
+
click.echo(f"Brightness: {dimmer_dev.brightness}")
|
|
218
210
|
else:
|
|
219
211
|
click.echo(f"Setting brightness to {brightness}")
|
|
220
|
-
return await
|
|
212
|
+
return await dimmer_dev.set_brightness(brightness)
|
|
221
213
|
|
|
222
214
|
|
|
223
215
|
@cli.command()
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import json
|
|
3
2
|
import logging
|
|
4
3
|
import socket
|
|
5
|
-
from typing import
|
|
4
|
+
from typing import Any, Type
|
|
6
5
|
from urllib.parse import urlparse
|
|
7
6
|
|
|
8
|
-
import ssdp
|
|
7
|
+
import ssdp # type: ignore
|
|
9
8
|
|
|
10
9
|
from swidget.swidgetdevice import DeviceType, SwidgetDevice
|
|
11
10
|
from .swidgetdimmer import SwidgetDimmer
|
|
@@ -39,7 +38,7 @@ class SwidgetProtocol(ssdp.SimpleServiceDiscoveryProtocol):
|
|
|
39
38
|
insert_type = headers["SERVER"].split(" ")[1].split("+")[1].split("/")[0]
|
|
40
39
|
friendly_name = headers["SERVER"].split("/")[2].strip('"')
|
|
41
40
|
devices[mac_address] = SwidgetDiscoveredDevice(mac_address, ip_address, friendly_name)
|
|
42
|
-
_LOGGER.debug(f"Swidget device '{friendly_name}' at {ip_address}")
|
|
41
|
+
_LOGGER.debug(f"Swidget device '{friendly_name}' at {ip_address}. {device_type}/{insert_type}")
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
async def discover_devices(timeout=RESPONSE_SEC):
|
|
@@ -65,7 +64,7 @@ async def discover_devices(timeout=RESPONSE_SEC):
|
|
|
65
64
|
return devices
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
async def discover_single(host: str, token_name: str, password: str, use_https: bool, use_websockets: bool) ->
|
|
67
|
+
async def discover_single(host: str, token_name: str, password: str, use_https: bool, use_websockets: bool) -> Any:
|
|
69
68
|
"""Discover a single device by the given IP address.
|
|
70
69
|
|
|
71
70
|
:param host: Hostname of device to query
|
|
@@ -88,16 +87,16 @@ async def discover_single(host: str, token_name: str, password: str, use_https:
|
|
|
88
87
|
return dev
|
|
89
88
|
|
|
90
89
|
|
|
91
|
-
def _get_device_class(device_type:
|
|
90
|
+
def _get_device_class(device_type: DeviceType) -> Type[SwidgetDevice]:
|
|
92
91
|
"""Find SmartDevice subclass for device described by passed data."""
|
|
93
|
-
if device_type ==
|
|
92
|
+
if device_type == DeviceType.Outlet:
|
|
94
93
|
return SwidgetOutlet
|
|
95
|
-
elif device_type ==
|
|
94
|
+
elif device_type == DeviceType.Switch:
|
|
96
95
|
return SwidgetSwitch
|
|
97
|
-
elif device_type ==
|
|
96
|
+
elif device_type == DeviceType.Dimmer:
|
|
98
97
|
return SwidgetDimmer
|
|
99
|
-
elif device_type ==
|
|
98
|
+
elif device_type == DeviceType.TimerSwitch: # This is the timer switch
|
|
100
99
|
return SwidgetTimerSwitch
|
|
101
|
-
elif device_type ==
|
|
100
|
+
elif device_type == DeviceType.RelaySwitch:
|
|
102
101
|
return SwidgetSwitch
|
|
103
102
|
raise SwidgetException("Unknown device type: %s" % device_type)
|
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
from aiohttp import ClientSession, TCPConnector
|
|
6
6
|
import asyncio
|
|
7
7
|
from enum import Enum
|
|
8
|
-
from typing import Dict, List
|
|
8
|
+
from typing import Any, Dict, List
|
|
9
9
|
|
|
10
10
|
from .exceptions import SwidgetException
|
|
11
11
|
from .websocket import SwidgetWebsocket
|
|
@@ -24,22 +24,21 @@ class DeviceType(Enum):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class SwidgetDevice:
|
|
27
|
-
def __init__(self, host, token_name, secret_key, use_https=True, use_websockets=True):
|
|
27
|
+
def __init__(self, host, token_name, secret_key, use_https=True, use_websockets=True) -> None:
|
|
28
28
|
self.token_name = token_name
|
|
29
29
|
self.ip_address = host
|
|
30
|
-
|
|
31
30
|
self.use_https = use_https
|
|
32
31
|
self.uri_scheme = 'https' if self.use_https is True else 'http'
|
|
33
32
|
self.secret_key = secret_key
|
|
34
33
|
self.use_websockets = use_websockets
|
|
35
34
|
self.device_type = DeviceType.Unknown
|
|
36
35
|
self._friendly_name = "Unknown Swidget Device"
|
|
37
|
-
self.assemblies =
|
|
36
|
+
self.assemblies: Dict[Any, Any] = dict()
|
|
38
37
|
headers = {self.token_name: self.secret_key,
|
|
39
38
|
'Connection': 'keep-alive'}
|
|
40
39
|
connector = TCPConnector(verify_ssl=False, force_close=True)
|
|
41
40
|
self._session = ClientSession(headers=headers, connector=connector)
|
|
42
|
-
self._last_update =
|
|
41
|
+
self._last_update: int = 0
|
|
43
42
|
if self.use_websockets:
|
|
44
43
|
self._websocket = SwidgetWebsocket(
|
|
45
44
|
host=self.ip_address,
|
|
@@ -48,18 +47,18 @@ class SwidgetDevice:
|
|
|
48
47
|
callback=self.message_callback,
|
|
49
48
|
session=self._session)
|
|
50
49
|
|
|
51
|
-
def get_websocket(self):
|
|
50
|
+
def get_websocket(self) -> SwidgetWebsocket | None:
|
|
52
51
|
if self.use_websockets:
|
|
53
52
|
return self._websocket
|
|
54
53
|
return None
|
|
55
54
|
|
|
56
|
-
def set_countdown_timer(self, minutes):
|
|
55
|
+
def set_countdown_timer(self, minutes) -> Any:
|
|
57
56
|
raise NotImplementedError()
|
|
58
57
|
|
|
59
|
-
async def connect(self):
|
|
58
|
+
async def connect(self) -> None:
|
|
60
59
|
await self._websocket.connect()
|
|
61
60
|
|
|
62
|
-
async def start(self):
|
|
61
|
+
async def start(self) -> None:
|
|
63
62
|
"""Start the websocket."""
|
|
64
63
|
_LOGGER.debug("SwidgetDevice.start()")
|
|
65
64
|
if self.use_websockets:
|
|
@@ -68,17 +67,17 @@ class SwidgetDevice:
|
|
|
68
67
|
_LOGGER.debug("Calling self.update() ")
|
|
69
68
|
await self.update()
|
|
70
69
|
|
|
71
|
-
async def stop(self):
|
|
70
|
+
async def stop(self) -> None:
|
|
72
71
|
"""Stop the websocket."""
|
|
73
72
|
_LOGGER.debug("SwidgetDevice.stop()")
|
|
74
73
|
if hasattr(self, '_websocket'):
|
|
75
74
|
await self._websocket.close()
|
|
76
75
|
await self._session.close()
|
|
77
76
|
|
|
78
|
-
async def close(self):
|
|
77
|
+
async def close(self) -> None:
|
|
79
78
|
await self.stop()
|
|
80
79
|
|
|
81
|
-
async def message_callback(self, message):
|
|
80
|
+
async def message_callback(self, message) -> None:
|
|
82
81
|
"""Entrypoint for a websocket callback"""
|
|
83
82
|
_LOGGER.debug("SwidgetDevice.message_callback() called")
|
|
84
83
|
if message["request_id"] == "summary":
|
|
@@ -90,7 +89,7 @@ class SwidgetDevice:
|
|
|
90
89
|
else:
|
|
91
90
|
_LOGGER.error(f"Unknown message type from websocket. Type given was: {message["request_id"]}")
|
|
92
91
|
|
|
93
|
-
async def get_summary(self):
|
|
92
|
+
async def get_summary(self) -> None:
|
|
94
93
|
"""Get a summary of the device over HTTP"""
|
|
95
94
|
_LOGGER.debug("SwidgetDevice.get_summary() called")
|
|
96
95
|
if self.use_websockets:
|
|
@@ -104,7 +103,7 @@ class SwidgetDevice:
|
|
|
104
103
|
summary = await response.json()
|
|
105
104
|
await self.process_summary(summary)
|
|
106
105
|
|
|
107
|
-
async def process_summary(self, summary):
|
|
106
|
+
async def process_summary(self, summary) -> None:
|
|
108
107
|
""" Process the data around the summary of the device"""
|
|
109
108
|
_LOGGER.debug("SwidgetDevice.process_summary() called")
|
|
110
109
|
_LOGGER.debug(f"Summary to process: {summary}")
|
|
@@ -115,12 +114,12 @@ class SwidgetDevice:
|
|
|
115
114
|
"host": SwidgetAssembly(summary["host"]),
|
|
116
115
|
"insert": SwidgetAssembly(summary["insert"]),
|
|
117
116
|
}
|
|
118
|
-
self.device_type = self.assemblies['host'].type
|
|
117
|
+
self.device_type = DeviceType(self.assemblies['host'].type)
|
|
119
118
|
self.insert_type = self.assemblies['insert'].type
|
|
120
119
|
self.id = self.assemblies['host'].id
|
|
121
120
|
self._last_update = int(time.time())
|
|
122
121
|
|
|
123
|
-
async def get_friendly_name(self):
|
|
122
|
+
async def get_friendly_name(self) -> None:
|
|
124
123
|
_LOGGER.debug("SwidgetDevice.get_friendly_name() called")
|
|
125
124
|
try:
|
|
126
125
|
async with self._session.get(
|
|
@@ -131,12 +130,12 @@ class SwidgetDevice:
|
|
|
131
130
|
name = {"name": f"Swidget {self.device_type} w/{self.insert_type} insert"}
|
|
132
131
|
await self.process_friendly_name(name['name'])
|
|
133
132
|
|
|
134
|
-
async def process_friendly_name(self, name):
|
|
133
|
+
async def process_friendly_name(self, name) -> None:
|
|
135
134
|
_LOGGER.debug("SwidgetDevice.process_friendly_name() called")
|
|
136
135
|
self._friendly_name = name
|
|
137
136
|
self._last_update = int(time.time())
|
|
138
137
|
|
|
139
|
-
async def get_state(self):
|
|
138
|
+
async def get_state(self) -> None:
|
|
140
139
|
""" Get the state of the device over HTTP"""
|
|
141
140
|
_LOGGER.debug("SwidgetDevice.get_state() called")
|
|
142
141
|
if self.use_websockets:
|
|
@@ -150,7 +149,7 @@ class SwidgetDevice:
|
|
|
150
149
|
state = await response.json()
|
|
151
150
|
await self.process_state(state)
|
|
152
151
|
|
|
153
|
-
async def process_state(self, state):
|
|
152
|
+
async def process_state(self, state) -> None:
|
|
154
153
|
""" Process any information about the state of the device or insert"""
|
|
155
154
|
# State is not always in the state (during callback)
|
|
156
155
|
_LOGGER.debug("SwidgetDevice.process_state() called")
|
|
@@ -167,9 +166,9 @@ class SwidgetDevice:
|
|
|
167
166
|
pass
|
|
168
167
|
self._last_update = int(time.time())
|
|
169
168
|
|
|
170
|
-
async def update(self):
|
|
169
|
+
async def update(self) -> None:
|
|
171
170
|
_LOGGER.debug("SwidgetDevice.update() called")
|
|
172
|
-
if self._last_update
|
|
171
|
+
if self._last_update == 0:
|
|
173
172
|
_LOGGER.debug("Performing the initial update to obtain sysinfo")
|
|
174
173
|
await self.get_summary()
|
|
175
174
|
await self.get_state()
|
|
@@ -182,25 +181,25 @@ class SwidgetDevice:
|
|
|
182
181
|
await self.get_summary()
|
|
183
182
|
await self.get_state()
|
|
184
183
|
|
|
185
|
-
async def send_config(self, payload: dict):
|
|
184
|
+
async def send_config(self, payload: dict) -> None:
|
|
186
185
|
_LOGGER.debug("SwidgetDevice.send_config() called")
|
|
187
186
|
data = json.dumps({"type":"config","request_id":"send_config", "payload": payload})
|
|
188
187
|
await self._websocket.send_str(data)
|
|
189
188
|
|
|
190
189
|
async def send_command(
|
|
191
190
|
self, assembly: str, component: str, function: str, command: dict
|
|
192
|
-
):
|
|
191
|
+
) -> None:
|
|
193
192
|
_LOGGER.debug("SwidgetDevice.send_command() called")
|
|
194
193
|
"""Send a command to the Swidget device either using a HTTP call or the existing websocket"""
|
|
195
194
|
data = {assembly: {"components": {component: {function: command}}}}
|
|
196
195
|
_LOGGER.debug(f"Command to send: {data}")
|
|
197
196
|
if self.use_websockets:
|
|
198
197
|
_LOGGER.debug("In websocket mode. Sending command over websocket")
|
|
199
|
-
|
|
198
|
+
command_data = json.dumps({"type": "command",
|
|
200
199
|
"request_id": "command",
|
|
201
200
|
"payload": data
|
|
202
201
|
})
|
|
203
|
-
await self._websocket.send_str(
|
|
202
|
+
await self._websocket.send_str(command_data)
|
|
204
203
|
else:
|
|
205
204
|
_LOGGER.debug("NOT in websocket mode, sending command over HTTP")
|
|
206
205
|
async with self._session.post(
|
|
@@ -214,7 +213,7 @@ class SwidgetDevice:
|
|
|
214
213
|
function_value = state[assembly]["components"][component][function]
|
|
215
214
|
self.assemblies[assembly].components[component].functions[function] = function_value # fmt: skip
|
|
216
215
|
|
|
217
|
-
async def ping(self):
|
|
216
|
+
async def ping(self) -> int | SwidgetException:
|
|
218
217
|
"""Ping the device to ensure it's devices
|
|
219
218
|
|
|
220
219
|
:raises SwidgetException: Raise the exception if there we are unable to connect to the Swidget device
|
|
@@ -229,7 +228,7 @@ class SwidgetDevice:
|
|
|
229
228
|
except:
|
|
230
229
|
raise SwidgetException
|
|
231
230
|
|
|
232
|
-
async def blink(self):
|
|
231
|
+
async def blink(self) -> Any:
|
|
233
232
|
"""Make the device LED blink
|
|
234
233
|
|
|
235
234
|
:raises SwidgetException: Raise the exception if there we are unable to connect to the Swidget device
|
|
@@ -244,7 +243,7 @@ class SwidgetDevice:
|
|
|
244
243
|
except:
|
|
245
244
|
raise SwidgetException
|
|
246
245
|
|
|
247
|
-
async def enable_debug_server(self):
|
|
246
|
+
async def enable_debug_server(self) -> Any:
|
|
248
247
|
"""Enable the Swidget local debug server
|
|
249
248
|
|
|
250
249
|
:raises SwidgetException: Raise the exception if there we are unable to connect to the Swidget device
|
|
@@ -259,7 +258,7 @@ class SwidgetDevice:
|
|
|
259
258
|
except:
|
|
260
259
|
raise SwidgetException
|
|
261
260
|
|
|
262
|
-
async def factory_reset(self):
|
|
261
|
+
async def factory_reset(self) -> Any:
|
|
263
262
|
"""Factory reset the Swidget device
|
|
264
263
|
|
|
265
264
|
:raises SwidgetException: Raise the exception if there we are unable to connect to the Swidget device
|
|
@@ -274,7 +273,7 @@ class SwidgetDevice:
|
|
|
274
273
|
except:
|
|
275
274
|
raise SwidgetException
|
|
276
275
|
|
|
277
|
-
async def check_for_updates(self):
|
|
276
|
+
async def check_for_updates(self) -> Any:
|
|
278
277
|
"""Tell the device to contact the Swidget servers to see if there is an available update
|
|
279
278
|
|
|
280
279
|
:raises SwidgetException: Raise the exception if there we are unable to connect to the Swidget device
|
|
@@ -289,7 +288,7 @@ class SwidgetDevice:
|
|
|
289
288
|
except:
|
|
290
289
|
raise SwidgetException
|
|
291
290
|
|
|
292
|
-
async def update_version(self, version):
|
|
291
|
+
async def update_version(self, version) -> Any:
|
|
293
292
|
"""Tell the device to download and apply an update
|
|
294
293
|
|
|
295
294
|
:raises SwidgetException: Raise the exception if there we are unable to connect to the Swidget device
|
|
@@ -303,7 +302,7 @@ class SwidgetDevice:
|
|
|
303
302
|
ssl=False,
|
|
304
303
|
data=json.dumps(data)
|
|
305
304
|
) as response:
|
|
306
|
-
return await response
|
|
305
|
+
return await response.json()
|
|
307
306
|
except:
|
|
308
307
|
raise SwidgetException
|
|
309
308
|
|
|
@@ -326,7 +325,7 @@ class SwidgetDevice:
|
|
|
326
325
|
"rssi": self.rssi
|
|
327
326
|
}
|
|
328
327
|
|
|
329
|
-
async def get_child_consumption(self, plug_id=0):
|
|
328
|
+
async def get_child_consumption(self, plug_id=0) -> Any:
|
|
330
329
|
"""Get the power consumption of a plug in watts."""
|
|
331
330
|
if plug_id == "all":
|
|
332
331
|
return_dict = {}
|
|
@@ -338,7 +337,7 @@ class SwidgetDevice:
|
|
|
338
337
|
return return_dict
|
|
339
338
|
return self.assemblies['host'].components[str(plug_id)].functions['power']['current']
|
|
340
339
|
|
|
341
|
-
async def total_consumption(self):
|
|
340
|
+
async def total_consumption(self) -> float:
|
|
342
341
|
"""Get the total power consumption in watts."""
|
|
343
342
|
total_consumption = 0
|
|
344
343
|
for id, properties in self.assemblies['host'].components.items():
|
|
@@ -346,7 +345,7 @@ class SwidgetDevice:
|
|
|
346
345
|
return total_consumption
|
|
347
346
|
|
|
348
347
|
@property
|
|
349
|
-
async def realtime_values(self):
|
|
348
|
+
async def realtime_values(self) -> Dict:
|
|
350
349
|
"""Get a dict of realtime value attributes from the insert and host
|
|
351
350
|
|
|
352
351
|
:return: A dictionary of insert sensor values and power consumption values
|
|
@@ -367,7 +366,7 @@ class SwidgetDevice:
|
|
|
367
366
|
try:
|
|
368
367
|
return list(self.assemblies['host'].components['0'].functions.keys())
|
|
369
368
|
except KeyError:
|
|
370
|
-
return
|
|
369
|
+
return list()
|
|
371
370
|
|
|
372
371
|
@property
|
|
373
372
|
def insert_features(self) -> List[str]:
|
|
@@ -375,9 +374,9 @@ class SwidgetDevice:
|
|
|
375
374
|
try:
|
|
376
375
|
return list(self.assemblies['insert'].components.keys())
|
|
377
376
|
except KeyError:
|
|
378
|
-
return
|
|
377
|
+
return list()
|
|
379
378
|
|
|
380
|
-
def get_function_values(self, function: str):
|
|
379
|
+
def get_function_values(self, function: str) -> Dict:
|
|
381
380
|
"""Return the values of an insert function."""
|
|
382
381
|
return_values = dict()
|
|
383
382
|
for function, data in self.assemblies['insert'].components[function].functions.items():
|
|
@@ -389,7 +388,7 @@ class SwidgetDevice:
|
|
|
389
388
|
return_values[function] = data['now']
|
|
390
389
|
return return_values
|
|
391
390
|
|
|
392
|
-
def get_sensor_value(self, function, sensor):
|
|
391
|
+
def get_sensor_value(self, function, sensor) -> float | str:
|
|
393
392
|
"""Return the value of a sensor given a function and sensor"""
|
|
394
393
|
if sensor == "occupied":
|
|
395
394
|
return self.assemblies['insert'].components[function].functions['occupied']['state']
|
|
@@ -399,22 +398,22 @@ class SwidgetDevice:
|
|
|
399
398
|
@property
|
|
400
399
|
def is_outlet(self) -> bool:
|
|
401
400
|
"""Return True if the device is an outlet."""
|
|
402
|
-
return self.device_type ==
|
|
401
|
+
return self.device_type == DeviceType.Outlet
|
|
403
402
|
|
|
404
403
|
@property
|
|
405
404
|
def is_switch(self) -> bool:
|
|
406
405
|
"""Return True if the device is a switch"""
|
|
407
|
-
return self.device_type ==
|
|
406
|
+
return self.device_type == DeviceType.Switch or self.device_type == DeviceType.TimerSwitch or self.device_type == DeviceType.RelaySwitch
|
|
408
407
|
|
|
409
408
|
@property
|
|
410
409
|
def is_pana_switch(self) -> bool:
|
|
411
410
|
"""Return True if the device is a pana_switch"""
|
|
412
|
-
return self.device_type ==
|
|
411
|
+
return self.device_type == DeviceType.TimerSwitch
|
|
413
412
|
|
|
414
413
|
@property
|
|
415
414
|
def is_dimmer(self) -> bool:
|
|
416
415
|
"""Return True if the device is a dimmer"""
|
|
417
|
-
return self.device_type ==
|
|
416
|
+
return self.device_type == DeviceType.Dimmer
|
|
418
417
|
|
|
419
418
|
@property
|
|
420
419
|
def is_dimmable(self) -> bool:
|
|
@@ -434,27 +433,27 @@ class SwidgetDevice:
|
|
|
434
433
|
return True
|
|
435
434
|
return False
|
|
436
435
|
|
|
437
|
-
async def turn_on(self):
|
|
436
|
+
async def turn_on(self) -> None:
|
|
438
437
|
"""Turn the device on."""
|
|
439
438
|
_LOGGER.debug("SwidgetDevice.turn_on() called")
|
|
440
439
|
await self.send_command(
|
|
441
440
|
assembly="host", component="0", function="toggle", command={"state": "on"}
|
|
442
441
|
)
|
|
443
442
|
|
|
444
|
-
async def turn_off(self):
|
|
443
|
+
async def turn_off(self) -> None:
|
|
445
444
|
"""Turn the device off."""
|
|
446
445
|
_LOGGER.debug("SwidgetDevice.turn_off() called")
|
|
447
446
|
await self.send_command(
|
|
448
447
|
assembly="host", component="0", function="toggle", command={"state": "off"}
|
|
449
448
|
)
|
|
450
449
|
|
|
451
|
-
async def turn_on_usb_insert(self):
|
|
450
|
+
async def turn_on_usb_insert(self) -> None:
|
|
452
451
|
"""Turn the USB insert on."""
|
|
453
452
|
await self.send_command(
|
|
454
453
|
assembly="insert", component="usb", function="toggle", command={"state": "on"}
|
|
455
454
|
)
|
|
456
455
|
|
|
457
|
-
async def turn_off_usb_insert(self):
|
|
456
|
+
async def turn_off_usb_insert(self) -> None:
|
|
458
457
|
"""Turn the USB insert off."""
|
|
459
458
|
await self.send_command(
|
|
460
459
|
assembly="insert", component="usb", function="toggle", command={"state": "off"}
|
|
@@ -468,8 +467,8 @@ class SwidgetDevice:
|
|
|
468
467
|
return True
|
|
469
468
|
return False
|
|
470
469
|
|
|
471
|
-
def __repr__(self):
|
|
472
|
-
if self._last_update
|
|
470
|
+
def __repr__(self) -> str:
|
|
471
|
+
if self._last_update == 0:
|
|
473
472
|
return f"<{self.device_type} at {self.ip_address} - update() needed>"
|
|
474
473
|
return f"<{self.device_type} model {self.model} at {self.ip_address}>"
|
|
475
474
|
|
|
@@ -28,14 +28,14 @@ class SwidgetDimmer(SwidgetDevice):
|
|
|
28
28
|
except KeyError:
|
|
29
29
|
return self.assemblies['host'].components["0"].functions["level"]["default"]
|
|
30
30
|
|
|
31
|
-
async def set_brightness(self, brightness):
|
|
31
|
+
async def set_brightness(self, brightness) -> None:
|
|
32
32
|
"""Set the brightness of the device."""
|
|
33
33
|
_LOGGER.debug("SwidgetDimmer.set_brightness() called")
|
|
34
34
|
await self.send_command(
|
|
35
35
|
assembly="host", component="0", function="level", command={"now": brightness}
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
async def set_default_brightness(self, brightness):
|
|
38
|
+
async def set_default_brightness(self, brightness) -> None:
|
|
39
39
|
_LOGGER.debug("SwidgetDimmer.set_default_brightness() called")
|
|
40
40
|
await self.send_command(
|
|
41
41
|
assembly="host", component="0", function="level", command={"default": brightness}
|
|
@@ -8,8 +8,4 @@ class SwidgetSwitch(SwidgetDevice):
|
|
|
8
8
|
|
|
9
9
|
def __init__(self, host, token_name: str, secret_key: str, use_https: bool, use_websockets: bool) -> None:
|
|
10
10
|
super().__init__(host=host, token_name=token_name, secret_key=secret_key, use_https=use_https, use_websockets=use_websockets)
|
|
11
|
-
self._device_type = DeviceType.Switch
|
|
12
|
-
|
|
13
|
-
async def current_consumption(self) -> float:
|
|
14
|
-
"""Get the current power consumption in watts."""
|
|
15
|
-
return sum([await plug.current_consumption() for plug in self.children])
|
|
11
|
+
self._device_type = DeviceType.Switch
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
2
4
|
from swidget.swidgetdevice import (
|
|
3
5
|
DeviceType,
|
|
4
6
|
)
|
|
@@ -13,7 +15,7 @@ class SwidgetTimerSwitch(SwidgetSwitch):
|
|
|
13
15
|
super().__init__(host=host, token_name=token_name, secret_key=secret_key, use_https=use_https, use_websockets=use_websockets)
|
|
14
16
|
self._device_type = DeviceType.TimerSwitch
|
|
15
17
|
|
|
16
|
-
async def set_countdown_timer(self, minutes):
|
|
18
|
+
async def set_countdown_timer(self, minutes) -> Any:
|
|
17
19
|
"""Set the countdown timer."""
|
|
18
20
|
_LOGGER.debug("SwidgetTimerSwitch.set_brightness() called")
|
|
19
21
|
await self.send_command(
|
|
@@ -33,7 +33,7 @@ class SwidgetWebsocket:
|
|
|
33
33
|
session=None,
|
|
34
34
|
use_security=True,
|
|
35
35
|
):
|
|
36
|
-
|
|
36
|
+
self.host = host
|
|
37
37
|
self.session = session or aiohttp.ClientSession()
|
|
38
38
|
self.use_security = use_security
|
|
39
39
|
self.uri = self.get_uri(host, token_name, secret_key)
|
|
@@ -52,7 +52,7 @@ class SwidgetWebsocket:
|
|
|
52
52
|
@property
|
|
53
53
|
def websocket(self) -> ClientWebSocketResponse | None:
|
|
54
54
|
"""Return the web socket."""
|
|
55
|
-
return self.
|
|
55
|
+
return self._client
|
|
56
56
|
|
|
57
57
|
def get_uri(self, host, token_name, secret_key):
|
|
58
58
|
"""Generate the websocket URI"""
|
|
@@ -75,16 +75,18 @@ class SwidgetWebsocket:
|
|
|
75
75
|
try:
|
|
76
76
|
self._client = await self.session.ws_connect(url=self.uri, headers=self.headers, verify_ssl=self._verify_ssl, heartbeat=30)
|
|
77
77
|
_LOGGER.debug("Websocket now connected")
|
|
78
|
-
except
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
except aiohttp.WSServerHandshakeError as handshake_error:
|
|
79
|
+
_LOGGER.error(f"Error occurred during websocket handshake: {handshake_error}")
|
|
80
|
+
raise
|
|
81
|
+
except aiohttp.ClientConnectionError as connection_error:
|
|
82
|
+
_LOGGER.error(f"Error connecting to the websocket server: {connection_error}")
|
|
83
|
+
raise
|
|
84
|
+
except socket.gaierror as gai_error:
|
|
85
|
+
_LOGGER.error(f"Error resolving host: {gai_error}")
|
|
86
|
+
raise
|
|
87
|
+
except Exception as e:
|
|
88
|
+
_LOGGER.error(f"An unexpected error occurred: {e}")
|
|
89
|
+
raise
|
|
88
90
|
self._receiver_task = asyncio.ensure_future(self.listen())
|
|
89
91
|
|
|
90
92
|
async def close(self) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|