python-mystrom 2.1.0__tar.gz → 2.4.0__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.
Files changed (31) hide show
  1. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/LICENSE +1 -1
  2. {python-mystrom-2.1.0/python_mystrom.egg-info → python_mystrom-2.4.0}/PKG-INFO +17 -16
  3. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/README.rst +1 -1
  4. python_mystrom-2.4.0/pymystrom/__init__.py +107 -0
  5. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/pymystrom/bulb.py +6 -7
  6. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/pymystrom/cli.py +9 -24
  7. python_mystrom-2.4.0/pymystrom/device_types.py +33 -0
  8. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/pymystrom/discovery.py +7 -12
  9. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/pymystrom/pir.py +4 -4
  10. python_mystrom-2.4.0/pymystrom/switch.py +177 -0
  11. python_mystrom-2.4.0/pyproject.toml +26 -0
  12. python-mystrom-2.1.0/CHANGELOG.rst +0 -69
  13. python-mystrom-2.1.0/MANIFEST.in +0 -2
  14. python-mystrom-2.1.0/PKG-INFO +0 -274
  15. python-mystrom-2.1.0/examples/example-bulb-hsv.py +0 -18
  16. python-mystrom-2.1.0/examples/example-bulb.py +0 -72
  17. python-mystrom-2.1.0/examples/example-discovery.py +0 -13
  18. python-mystrom-2.1.0/examples/example-get-data-bulb.py +0 -12
  19. python-mystrom-2.1.0/examples/example-pir.py +0 -43
  20. python-mystrom-2.1.0/examples/example-switch.py +0 -37
  21. python-mystrom-2.1.0/pymystrom/__init__.py +0 -63
  22. python-mystrom-2.1.0/pymystrom/switch.py +0 -114
  23. python-mystrom-2.1.0/python_mystrom.egg-info/SOURCES.txt +0 -25
  24. python-mystrom-2.1.0/python_mystrom.egg-info/dependency_links.txt +0 -1
  25. python-mystrom-2.1.0/python_mystrom.egg-info/entry_points.txt +0 -2
  26. python-mystrom-2.1.0/python_mystrom.egg-info/requires.txt +0 -4
  27. python-mystrom-2.1.0/python_mystrom.egg-info/top_level.txt +0 -1
  28. python-mystrom-2.1.0/python_mystrom.egg-info/zip-safe +0 -1
  29. python-mystrom-2.1.0/setup.cfg +0 -4
  30. python-mystrom-2.1.0/setup.py +0 -48
  31. {python-mystrom-2.1.0 → python_mystrom-2.4.0}/pymystrom/exceptions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2022 Fabian Affolter <fabian@affolter-engineering.ch>
3
+ Copyright (c) 2015-2025 Fabian Affolter <fabian@affolter-engineering.ch>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,23 +1,23 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: python-mystrom
3
- Version: 2.1.0
3
+ Version: 2.4.0
4
4
  Summary: Asynchronous Python API client for interacting with myStrom devices
5
- Home-page: https://github.com/home-assistant-ecosystem/python-mystrom
5
+ License: MIT
6
+ Keywords: myStrom,API,client,asynchronous
6
7
  Author: Fabian Affolter
7
8
  Author-email: fabian@affolter-engineering.ch
8
- License: MIT
9
- Classifier: Development Status :: 3 - Alpha
10
- Classifier: Environment :: Console
11
- Classifier: Intended Audience :: Developers
9
+ Requires-Python: >=3.11
12
10
  Classifier: License :: OSI Approved :: MIT License
13
- Classifier: Operating System :: MacOS :: MacOS X
14
- Classifier: Operating System :: Microsoft :: Windows
15
- Classifier: Operating System :: POSIX
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Topic :: Utilities
19
- Requires-Python: >=3.9
20
- License-File: LICENSE
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: aiohttp
16
+ Requires-Dist: click
17
+ Requires-Dist: requests
18
+ Project-URL: Homepage, https://github.com/home-assistant-ecosystem/python-mystrom
19
+ Project-URL: Repository, https://github.com/home-assistant-ecosystem/python-mystrom
20
+ Description-Content-Type: text/x-rst
21
21
 
22
22
  python-mystrom |License| |PyPI|
23
23
  ===================================
@@ -63,7 +63,7 @@ be present in the ``unstable`` channel.
63
63
 
64
64
  .. code:: bash
65
65
 
66
- $ nix-env -iA nixos.python39Packages.python-mystrom
66
+ $ nix-env -iA nixos.python3Packages.python-mystrom
67
67
 
68
68
 
69
69
  Plug/switch
@@ -272,3 +272,4 @@ License
272
272
  .. |PyPI| image:: https://img.shields.io/pypi/v/python-mystrom.svg
273
273
  :target: https://pypi.python.org/pypi/python-mystrom
274
274
  :alt: PyPI release
275
+
@@ -42,7 +42,7 @@ be present in the ``unstable`` channel.
42
42
 
43
43
  .. code:: bash
44
44
 
45
- $ nix-env -iA nixos.python39Packages.python-mystrom
45
+ $ nix-env -iA nixos.python3Packages.python-mystrom
46
46
 
47
47
 
48
48
  Plug/switch
@@ -0,0 +1,107 @@
1
+ """Base details for the myStrom Python bindings."""
2
+
3
+ import asyncio
4
+ import socket
5
+ from typing import Any, Mapping, Optional
6
+
7
+ import aiohttp
8
+ from yarl import URL
9
+
10
+ from .exceptions import MyStromConnectionError
11
+
12
+ TIMEOUT = 10
13
+ USER_AGENT = "PythonMyStrom/1.0"
14
+
15
+
16
+ async def _request(
17
+ self,
18
+ uri: str,
19
+ method: str = "GET",
20
+ data: Optional[Any] = None,
21
+ json_data: Optional[dict] = None,
22
+ params: Optional[Mapping[str, str]] = None,
23
+ ) -> Any:
24
+ """Handle a request to the myStrom device."""
25
+ headers = {
26
+ "User-Agent": USER_AGENT,
27
+ "Accept": "application/json, text/plain, */*",
28
+ }
29
+
30
+ if self._session is None:
31
+ self._session = aiohttp.ClientSession()
32
+ self._close_session = True
33
+
34
+ try:
35
+ response = await asyncio.wait_for(
36
+ self._session.request(
37
+ method,
38
+ uri,
39
+ data=data,
40
+ json=json_data,
41
+ params=params,
42
+ headers=headers,
43
+ ),
44
+ timeout=TIMEOUT,
45
+ )
46
+ except asyncio.TimeoutError as exception:
47
+ raise MyStromConnectionError(
48
+ "Timeout occurred while connecting to myStrom device."
49
+ ) from exception
50
+ except (aiohttp.ClientError, socket.gaierror) as exception:
51
+ raise MyStromConnectionError(
52
+ "Error occurred while communicating with myStrom device."
53
+ ) from exception
54
+
55
+ content_type = response.headers.get("Content-Type", "")
56
+ if (response.status // 100) in [4, 5]:
57
+ response.close()
58
+
59
+ if "application/json" in content_type:
60
+ response_json = await response.json()
61
+ return response_json
62
+
63
+ return response.text
64
+
65
+
66
+ class MyStromDevice:
67
+ """A class for a myStrom device."""
68
+
69
+ def __init__(
70
+ self,
71
+ host,
72
+ session: aiohttp.client.ClientSession = None,
73
+ ):
74
+ """Initialize the device."""
75
+ self._close_session = False
76
+ self._host = host
77
+ self._session = session
78
+ self.uri = URL.build(scheme="http", host=self._host)
79
+
80
+ async def get_device_info(self) -> dict:
81
+ """Get the device info of a myStrom device."""
82
+ url = URL(self.uri).join(URL("api/v1/info"))
83
+ response = await _request(self, uri=url)
84
+ if not isinstance(response, dict):
85
+ # Fall back to the old API version if the device runs with old firmware
86
+ url = URL(self.uri).join(URL("info.json"))
87
+ response = await _request(self, uri=url)
88
+ return response
89
+
90
+ async def close(self) -> None:
91
+ """Close an open client session."""
92
+ if self._session and self._close_session:
93
+ await self._session.close()
94
+
95
+ async def __aenter__(self) -> "MyStromDevice":
96
+ """Async enter."""
97
+ return self
98
+
99
+ async def __aexit__(self, *exc_info) -> None:
100
+ """Async exit."""
101
+ await self.close()
102
+
103
+
104
+ async def get_device_info(host: str) -> dict:
105
+ """Get the device info of a myStrom device."""
106
+ async with MyStromDevice(host) as device:
107
+ return await device.get_device_info()
@@ -1,10 +1,11 @@
1
1
  """Support for communicating with myStrom bulbs."""
2
+
2
3
  import asyncio
3
4
  import logging
5
+ from typing import Optional
4
6
 
5
7
  import aiohttp
6
8
  from yarl import URL
7
- from typing import Any, Dict, Iterable, List, Optional, Union
8
9
 
9
10
  from . import _request as request
10
11
 
@@ -21,7 +22,7 @@ class MyStromBulb:
21
22
  host: str,
22
23
  mac: str,
23
24
  session: aiohttp.client.ClientSession = None,
24
- ):
25
+ ) -> None:
25
26
  """Initialize the bulb."""
26
27
  self._close_session = False
27
28
  self._host = host
@@ -36,11 +37,9 @@ class MyStromBulb:
36
37
  self._bulb_type = None
37
38
  self._state = None
38
39
  self._transition_time = 0
39
- self.uri = (
40
- URL.build(scheme="http", host=self._host).join(URI_BULB) / self._mac
41
- )
40
+ self.uri = URL.build(scheme="http", host=self._host).join(URI_BULB) / self._mac
42
41
 
43
- async def get_state(self) -> object:
42
+ async def get_state(self) -> None:
44
43
  """Get the state of the bulb."""
45
44
  response = await request(self, uri=self.uri)
46
45
  self._consumption = response[self._mac]["power"]
@@ -57,7 +56,7 @@ class MyStromBulb:
57
56
  return self._firmware
58
57
 
59
58
  @property
60
- def mac(self) -> float:
59
+ def mac(self) -> str:
61
60
  """Return the MAC address."""
62
61
  return self._mac
63
62
 
@@ -1,4 +1,5 @@
1
1
  """Command-line tool for working with myStrom devices."""
2
+
2
3
  import click
3
4
  import requests
4
5
  import asyncio
@@ -50,9 +51,7 @@ def read_config(ip, mac):
50
51
  """Read the current configuration of a myStrom device."""
51
52
  click.echo("Read configuration from %s" % ip)
52
53
  try:
53
- request = requests.get(
54
- "http://{}/{}/{}/".format(ip, URI, mac), timeout=TIMEOUT
55
- )
54
+ request = requests.get("http://{}/{}/{}/".format(ip, URI, mac), timeout=TIMEOUT)
56
55
  click.echo(request.json())
57
56
  except requests.exceptions.ConnectionError:
58
57
  click.echo("Communication issue with the device")
@@ -87,9 +86,7 @@ def button():
87
86
  @click.option(
88
87
  "--long", prompt="URL for a long tab", default="", help="URL for a long tab."
89
88
  )
90
- @click.option(
91
- "--touch", prompt="URL for a touch", default="", help="URL for a touch."
92
- )
89
+ @click.option("--touch", prompt="URL for a touch", default="", help="URL for a touch.")
93
90
  def write_config(ip, mac, single, double, long, touch):
94
91
  """Write the current configuration of a myStrom button."""
95
92
  click.echo("Write configuration to device %s" % ip)
@@ -208,9 +205,7 @@ def read_config(ip, mac):
208
205
  """Read the current configuration of a myStrom WiFi Button."""
209
206
  click.echo("Read the configuration of button %s..." % ip)
210
207
  try:
211
- request = requests.get(
212
- "http://{}/{}/{}/".format(ip, URI, mac), timeout=TIMEOUT
213
- )
208
+ request = requests.get("http://{}/{}/{}/".format(ip, URI, mac), timeout=TIMEOUT)
214
209
  click.echo(request.json())
215
210
  except requests.exceptions.ConnectionError:
216
211
  click.echo("Communication issue with the device. No action performed")
@@ -223,9 +218,7 @@ def bulb():
223
218
 
224
219
  @bulb.command("on")
225
220
  @coro
226
- @click.option(
227
- "--ip", prompt="IP address of the bulb", help="IP address of the bulb."
228
- )
221
+ @click.option("--ip", prompt="IP address of the bulb", help="IP address of the bulb.")
229
222
  @click.option(
230
223
  "--mac", prompt="MAC address of the bulb", help="MAC address of the bulb."
231
224
  )
@@ -237,9 +230,7 @@ async def on(ip, mac):
237
230
 
238
231
  @bulb.command("color")
239
232
  @coro
240
- @click.option(
241
- "--ip", prompt="IP address of the bulb", help="IP address of the bulb."
242
- )
233
+ @click.option("--ip", prompt="IP address of the bulb", help="IP address of the bulb.")
243
234
  @click.option(
244
235
  "--mac", prompt="MAC address of the bulb", help="MAC address of the bulb."
245
236
  )
@@ -264,9 +255,7 @@ async def color(ip, mac, hue, saturation, value):
264
255
 
265
256
  @bulb.command("off")
266
257
  @coro
267
- @click.option(
268
- "--ip", prompt="IP address of the bulb", help="IP address of the bulb."
269
- )
258
+ @click.option("--ip", prompt="IP address of the bulb", help="IP address of the bulb.")
270
259
  @click.option(
271
260
  "--mac", prompt="MAC address of the bulb", help="MAC address of the bulb."
272
261
  )
@@ -278,9 +267,7 @@ async def off(ip, mac):
278
267
 
279
268
  @bulb.command("flash")
280
269
  @coro
281
- @click.option(
282
- "--ip", prompt="IP address of the bulb", help="IP address of the bulb."
283
- )
270
+ @click.option("--ip", prompt="IP address of the bulb", help="IP address of the bulb.")
284
271
  @click.option(
285
272
  "--mac", prompt="MAC address of the bulb", help="MAC address of the bulb."
286
273
  )
@@ -298,9 +285,7 @@ async def flash(ip, mac, time):
298
285
 
299
286
  @bulb.command("rainbow")
300
287
  @coro
301
- @click.option(
302
- "--ip", prompt="IP address of the bulb", help="IP address of the bulb."
303
- )
288
+ @click.option("--ip", prompt="IP address of the bulb", help="IP address of the bulb.")
304
289
  @click.option(
305
290
  "--mac", prompt="MAC address of the bulb", help="MAC address of the bulb."
306
291
  )
@@ -0,0 +1,33 @@
1
+ """
2
+ Device types.
3
+
4
+ See https://api.mystrom.ch/#f37a4be7-0233-4d93-915e-c6f92656f129
5
+ """
6
+
7
+ DEVICE_MAPPING_NUMERIC = {
8
+ 101: "Switch CH v1",
9
+ 102: "Bulb",
10
+ 103: "Button+",
11
+ 104: "Button",
12
+ 105: "LED Strip",
13
+ 106: "Switch CH v2",
14
+ 107: "Switch EU",
15
+ 110: "Motion Sensor",
16
+ 113: "modulo® STECCO / CUBO",
17
+ 118: "Button Plus 2nd",
18
+ 120: "Switch Zero",
19
+ }
20
+
21
+ DEVICE_MAPPING_LITERAL = {
22
+ "WSW": DEVICE_MAPPING_NUMERIC[101],
23
+ "WRB": DEVICE_MAPPING_NUMERIC[102],
24
+ "WBP": DEVICE_MAPPING_NUMERIC[103],
25
+ "WBS": DEVICE_MAPPING_NUMERIC[104],
26
+ "WRS": DEVICE_MAPPING_NUMERIC[105],
27
+ "WS2": DEVICE_MAPPING_NUMERIC[106],
28
+ "WSE": DEVICE_MAPPING_NUMERIC[107],
29
+ "WMS": DEVICE_MAPPING_NUMERIC[110],
30
+ "WLL": DEVICE_MAPPING_NUMERIC[113],
31
+ "BP2": DEVICE_MAPPING_NUMERIC[118],
32
+ "LCS": DEVICE_MAPPING_NUMERIC[120],
33
+ }
@@ -1,13 +1,12 @@
1
1
  """Support for discovering myStrom devices."""
2
+
2
3
  import asyncio
3
4
  import logging
4
- from typing import Optional, List
5
+ from typing import List, Optional
5
6
 
6
- _LOGGER = logging.getLogger(__name__)
7
+ from .device_types import DEVICE_MAPPING_NUMERIC
7
8
 
8
- DEVICE_MAPPING = {
9
- "102": "myStrom Bulb",
10
- }
9
+ _LOGGER = logging.getLogger(__name__)
11
10
 
12
11
 
13
12
  class DiscoveredDevice(object):
@@ -23,19 +22,15 @@ class DiscoveredDevice(object):
23
22
  @staticmethod
24
23
  def create_from_announce_msg(raw_addr, announce_msg):
25
24
  """Create announce message."""
26
- _LOGGER.debug(
27
- "Received announce message '%s' from %s ", announce_msg, raw_addr
28
- )
25
+ _LOGGER.debug("Received announce message '%s' from %s ", announce_msg, raw_addr)
29
26
  if len(announce_msg) != 8:
30
27
  raise RuntimeError("Unexpected announcement, '%s'" % announce_msg)
31
28
 
32
- device = DiscoveredDevice(
33
- host=raw_addr[0], mac=announce_msg[0:6].hex(":")
34
- )
29
+ device = DiscoveredDevice(host=raw_addr[0], mac=announce_msg[0:6].hex(":"))
35
30
  device.type = announce_msg[6]
36
31
 
37
32
  if device.type == "102":
38
- device.hardware = DEVICE_MAPPING[str(announce_msg[6])]
33
+ device.hardware = DEVICE_MAPPING_NUMERIC[int(announce_msg[6])]
39
34
  else:
40
35
  device.hardware = "non_mystrom"
41
36
  status = announce_msg[7]
@@ -1,7 +1,9 @@
1
1
  """Support for communicating with myStrom PIRs."""
2
+
3
+ from typing import Any, Dict, Iterable, List, Optional, Union
4
+
2
5
  import aiohttp
3
6
  from yarl import URL
4
- from typing import Any, Dict, Iterable, List, Optional, Union
5
7
 
6
8
  from . import _request as request
7
9
 
@@ -11,9 +13,7 @@ URI_PIR = URL("api/v1/")
11
13
  class MyStromPir:
12
14
  """A class for a myStrom PIR."""
13
15
 
14
- def __init__(
15
- self, host: str, session: aiohttp.client.ClientSession = None
16
- ) -> None:
16
+ def __init__(self, host: str, session: aiohttp.client.ClientSession = None) -> None:
17
17
  """Initialize the switch."""
18
18
  self._close_session = False
19
19
  self._host = host
@@ -0,0 +1,177 @@
1
+ """Support for communicating with myStrom plugs/switches."""
2
+
3
+ from typing import Optional, Union
4
+
5
+ import aiohttp
6
+ from yarl import URL
7
+
8
+ from . import _request as request
9
+ from .device_types import DEVICE_MAPPING_LITERAL, DEVICE_MAPPING_NUMERIC
10
+
11
+
12
+ class MyStromSwitch:
13
+ """A class for a myStrom switch/plug."""
14
+
15
+ def __init__(self, host: str, session: aiohttp.client.ClientSession = None) -> None:
16
+ """Initialize the switch."""
17
+ self._close_session = False
18
+ self._host = host
19
+ self._session = session
20
+ self._consumption = 0
21
+ self._consumedWs = 0
22
+ self._boot_id = None
23
+ self._energy_since_boot = None
24
+ self._time_since_boot = None
25
+ self._state = None
26
+ self._temperature = None
27
+ self._firmware = None
28
+ self._mac = None
29
+ self._device_type: Optional[Union[str, int]] = None
30
+ self.uri = URL.build(scheme="http", host=self._host)
31
+
32
+ async def turn_on(self) -> None:
33
+ """Turn the relay on."""
34
+ parameters = {"state": "1"}
35
+ url = URL(self.uri).join(URL("relay"))
36
+ await request(self, uri=url, params=parameters)
37
+ await self.get_state()
38
+
39
+ async def turn_off(self) -> None:
40
+ """Turn the relay off."""
41
+ parameters = {"state": "0"}
42
+ url = URL(self.uri).join(URL("relay"))
43
+ await request(self, uri=url, params=parameters)
44
+ await self.get_state()
45
+
46
+ async def toggle(self) -> None:
47
+ """Toggle the relay."""
48
+ url = URL(self.uri).join(URL("toggle"))
49
+ await request(self, uri=url)
50
+ await self.get_state()
51
+
52
+ async def get_state(self) -> None:
53
+ """Get the details from the switch/plug."""
54
+ url = URL(self.uri).join(URL("report"))
55
+ response = await request(self, uri=url)
56
+ try:
57
+ self._consumption = response["power"]
58
+ except KeyError:
59
+ self._consumption = None
60
+ try:
61
+ self._consumedWs = response["Ws"]
62
+ except KeyError:
63
+ self._consumedWs = None
64
+ try:
65
+ self._boot_id = response["boot_id"]
66
+ except KeyError:
67
+ self._boot_id = None
68
+ try:
69
+ self._energy_since_boot = response["energy_since_boot"]
70
+ except KeyError:
71
+ self._energy_since_boot = None
72
+ try:
73
+ self._time_since_boot = response["time_since_boot"]
74
+ except KeyError:
75
+ self._time_since_boot = None
76
+ self._state = response["relay"]
77
+ try:
78
+ self._temperature = response["temperature"]
79
+ except KeyError:
80
+ self._temperature = None
81
+
82
+ # Try the new API (Devices with newer firmware)
83
+ url = URL(self.uri).join(URL("api/v1/info"))
84
+ response = await request(self, uri=url)
85
+ if not isinstance(response, dict):
86
+ # Fall back to the old API version if the device runs with old firmware
87
+ url = URL(self.uri).join(URL("info.json"))
88
+ response = await request(self, uri=url)
89
+
90
+ self._firmware = response["version"]
91
+ self._mac = response["mac"]
92
+ self._device_type = response["type"]
93
+
94
+ @property
95
+ def device_type(self) -> Optional[str]:
96
+ """Return the device type as string (e.g. "Switch CH v1" or "Button+")."""
97
+ if isinstance(self._device_type, int):
98
+ return DEVICE_MAPPING_NUMERIC.get(self._device_type)
99
+ elif isinstance(self._device_type, str):
100
+ return DEVICE_MAPPING_LITERAL.get(self._device_type)
101
+ return None
102
+
103
+ @property
104
+ def relay(self) -> bool:
105
+ """Return the relay state."""
106
+ return bool(self._state)
107
+
108
+ @property
109
+ def consumption(self) -> Optional[float]:
110
+ """Return the current power consumption in mWh."""
111
+ if self._consumption is not None:
112
+ return round(self._consumption, 1)
113
+
114
+ return self._consumption
115
+
116
+ @property
117
+ def consumedWs(self) -> Optional[float]:
118
+ """The average of energy consumed per second since last report call."""
119
+ if self._consumedWs is not None:
120
+ return round(self._consumedWs, 1)
121
+
122
+ return self._consumedWs
123
+
124
+ @property
125
+ def boot_id(self) -> Optional[str]:
126
+ """A unique identifier to distinguish whether the energy counter has been reset."""
127
+ return self._boot_id
128
+
129
+ @property
130
+ def energy_since_boot(self) -> Optional[float]:
131
+ """The total energy in watt seconds (Ws) that has been measured since the last power-up or restart of the device."""
132
+ if self._energy_since_boot is not None:
133
+ return round(self._energy_since_boot, 2)
134
+
135
+ return self._energy_since_boot
136
+
137
+ @property
138
+ def time_since_boot(self) -> Optional[int]:
139
+ """The time in seconds that has elapsed since the last start or restart of the device."""
140
+ return self._time_since_boot
141
+
142
+ @property
143
+ def firmware(self) -> Optional[str]:
144
+ """Return the current firmware."""
145
+ return self._firmware
146
+
147
+ @property
148
+ def mac(self) -> Optional[str]:
149
+ """Return the MAC address."""
150
+ return self._mac
151
+
152
+ @property
153
+ def temperature(self) -> Optional[float]:
154
+ """Return the current temperature in Celsius."""
155
+ if self._temperature is not None:
156
+ return round(self._temperature, 1)
157
+
158
+ return self._temperature
159
+
160
+ async def get_temperature_full(self) -> str:
161
+ """Get current temperature in celsius."""
162
+ url = URL(self.uri).join(URL("temp"))
163
+ response = await request(self, uri=url)
164
+ return response
165
+
166
+ async def close(self) -> None:
167
+ """Close an open client session."""
168
+ if self._session and self._close_session:
169
+ await self._session.close()
170
+
171
+ async def __aenter__(self) -> "MyStromSwitch":
172
+ """Async enter."""
173
+ return self
174
+
175
+ async def __aexit__(self, *exc_info) -> None:
176
+ """Async exit."""
177
+ await self.close()
@@ -0,0 +1,26 @@
1
+ [tool.poetry]
2
+ name = "python-mystrom"
3
+ version = "2.4.0"
4
+ description = "Asynchronous Python API client for interacting with myStrom devices"
5
+ authors = ["Fabian Affolter <fabian@affolter-engineering.ch>"]
6
+ license = "MIT"
7
+ readme = "README.rst"
8
+ homepage = "https://github.com/home-assistant-ecosystem/python-mystrom"
9
+ repository = "https://github.com/home-assistant-ecosystem/python-mystrom"
10
+ keywords = ["myStrom", "API", "client", "asynchronous"]
11
+ packages = [
12
+ { include = "pymystrom" }
13
+ ]
14
+
15
+ [tool.poetry.dependencies]
16
+ python = ">=3.11"
17
+ aiohttp = "*"
18
+ click = "*"
19
+ requests = "*"
20
+
21
+ [build-system]
22
+ requires = ["poetry-core>=1.0.0"]
23
+ build-backend = "poetry.core.masonry.api"
24
+
25
+ [tool.poetry.scripts]
26
+ pymystrom = "pymystrom.cli:main"
@@ -1,69 +0,0 @@
1
- Changelog
2
- =========
3
-
4
- 2.1.0 (2022-11-26)
5
- ------------------
6
-
7
- - Add dd consumed energy to switch (thanks @OneCyrus)
8
-
9
- 2.0.0 (2020-11-12)
10
- ------------------
11
-
12
- - Update the CLI to work with the bulbs
13
- - Add support for Motion/PIR sensors
14
- - Add support for device discovery
15
-
16
- 1.1.3 (2020-06-08)
17
- ------------------
18
-
19
- - Improve temperature handling (Switch HW v2)
20
-
21
- 1.1.2 (2020-04-12)
22
- ------------------
23
-
24
- - Minor changes and fixes
25
-
26
- 1.1.1 (2020-04-12)
27
- ------------------
28
-
29
- - Fix typo
30
-
31
-
32
- 1.1.1 (2020-04-11)
33
- ------------------
34
-
35
- - Minor fixes
36
-
37
- 1.1.0 (2020-04-10)
38
- ------------------
39
-
40
- - Add new features for bulb
41
- - Add new features for switch/plug
42
-
43
- 1.0.0 (2020-01-05)
44
- ------------------
45
-
46
- - Full asynchronous now
47
- - Move to aiohttp
48
- - Update file header
49
-
50
- 0.5.0 (2019-02-27)
51
- ------------------
52
-
53
- - Add support for temperature provided by Switch v2
54
-
55
- 0.4.4 (2018-06-07)
56
- ------------------
57
-
58
- - Fix install_requires
59
-
60
- 0.4.3 (2018-06-07)
61
- ------------------
62
-
63
- - Update README
64
-
65
- 0.4.2 (2018-03-27)
66
- ------------------
67
-
68
- - Remove subprocess
69
- - Add CLI