Skip to content

Commit bcae1ea

Browse files
authored
Merge pull request #10 from 2Fake/development
Development
2 parents 0c58c5b + 226920b commit bcae1ea

16 files changed

+1168
-53
lines changed

.github/workflows/pythonpublish.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Upload Python Package
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- name: Set up Python
13+
uses: actions/setup-python@v1
14+
with:
15+
python-version: "3.7"
16+
- name: Install dependencies
17+
run: |
18+
python -m pip install --upgrade pip
19+
pip install setuptools wheel twine
20+
- name: Build and publish
21+
env:
22+
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
23+
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
24+
run: |
25+
python setup.py sdist bdist_wheel
26+
twine upload dist/*

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# devolo PLC API
22

33
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/2Fake/devolo_plc_api/Python%20package)](https://github.com/2Fake/devolo_plc_api/actions?query=workflow%3A%22Python+package%22)
4+
[![PyPI - Downloads](https://img.shields.io/pypi/dd/devolo-plc-api)](https://pypi.org/project/devolo-plc-api/)
45
[![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/2Fake/devolo_plc_api)](https://codeclimate.com/github/2Fake/devolo_plc_api)
56
[![Coverage Status](https://coveralls.io/repos/github/2Fake/devolo_plc_api/badge.svg?branch=development)](https://coveralls.io/github/2Fake/devolo_plc_api?branch=development)
67

@@ -59,4 +60,23 @@ python setup.py test
5960

6061
## Usage
6162

62-
All features we currently support are shown in our [example.py](https://github.com/2Fake/devolo_plc_api/blob/master/example.py)
63+
All features we currently support are shown in our examples. If you want to use the package asynchronously, please check [example_async.py](https://github.com/2Fake/devolo_plc_api/blob/master/example_async.py). If you want to use it synchronously, please check [example_sync.py](https://github.com/2Fake/devolo_plc_api/blob/master/example_sync.py).
64+
65+
## Supported device
66+
67+
The following devolo devices were queried with at least one call to verify functionality:
68+
69+
* Magic 2 WiFi next
70+
* Magic 2 WiFi 2-1
71+
* Magic 2 LAN triple
72+
* Magic 2 DinRail
73+
* Magic 2 LAN 1-1
74+
* Magic 1 WiFi mini
75+
* Magic 1 WiFi 2-1
76+
* Magic 1 LAN 1-1
77+
* dLAN 1200+ WiFi ac
78+
* dLAN 550+ Wifi
79+
* dLAN 550 WiFi
80+
* dLAN 500 WiFi
81+
82+
However, other devices might work, some might have a limited functionality. Also firmware version will matter. If you discover something weird, [we want to know](https://github.com/2Fake/devolo_plc_api/issues).

devolo_plc_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.1.0"
1+
__version__ = "0.2.0"

devolo_plc_api/device.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ async def _get_device_info(self):
8383
self.product = self._info[service_type].get("Product", "")
8484

8585
self.device = DeviceApi(ip=self.ip,
86+
port=self._info[service_type]['Port'],
8687
session=self._session,
8788
path=self._info[service_type]['Path'],
8889
version=self._info[service_type]['Version'],
@@ -101,6 +102,7 @@ async def _get_plcnet_info(self):
101102
self.technology = self._info[service_type].get("PlcTechnology", "")
102103

103104
self.plcnet = PlcNetApi(ip=self.ip,
105+
port=self._info[service_type]['Port'],
104106
session=self._session,
105107
path=self._info[service_type]['Path'],
106108
version=self._info[service_type]['Version'],
@@ -121,6 +123,8 @@ def _state_change(self, zeroconf: Zeroconf, service_type: str, name: str, state_
121123
self.ip in [socket.inet_ntoa(address) for address in service_info.addresses]:
122124
self._logger.debug(f"Adding service info of {service_type}")
123125

126+
self._info[service_type]['Port'] = service_info.port
127+
124128
# The answer is a byte string, that concatenates key-value pairs with their length as two byte hex value.
125129
total_length = len(service_info.text)
126130
offset = 0

devolo_plc_api/device_api/deviceapi.py

Lines changed: 163 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ..exceptions.feature import FeatureNotSupported
88
from . import devolo_idl_proto_deviceapi_wifinetwork_pb2
99
from . import devolo_idl_proto_deviceapi_ledsettings_pb2
10+
from . import devolo_idl_proto_deviceapi_updatefirmware_pb2
1011

1112

1213
class DeviceApi(Protobuf):
@@ -20,13 +21,13 @@ class DeviceApi(Protobuf):
2021
:param features: Feature, the device has
2122
"""
2223

23-
def __init__(self, ip: str, session: Client, path: str, version: str, features: str, password: str):
24+
def __init__(self, ip: str, port: int, session: Client, path: str, version: str, features: str, password: str):
2425
self._ip = ip
25-
self._port = 14791
26+
self._port = port
2627
self._session = session
2728
self._path = path
2829
self._version = version
29-
self._features = features.split(",") if features else []
30+
self._features = features.split(",") if features else ['reset', 'update', 'led', 'intmtg']
3031
self._user = "devolo"
3132
self._password = password
3233
self._logger = logging.getLogger(self.__class__.__name__)
@@ -45,24 +46,37 @@ def wrapper(self, *args, **kwargs):
4546

4647

4748
@_feature("led")
48-
async def async_get_led_setting(self):
49-
""" Get LED setting asynchronously. """
49+
async def async_get_led_setting(self) -> dict:
50+
"""
51+
Get LED setting asynchronously. This feature only works on devices, that announce the led feature.
52+
53+
return: LED settings
54+
"""
5055
led_setting = devolo_idl_proto_deviceapi_ledsettings_pb2.LedSettingsGet()
5156
response = await self._async_get("LedSettingsGet")
5257
led_setting.FromString(await response.aread())
5358
return self._message_to_dict(led_setting)
5459

5560
@_feature("led")
5661
def get_led_setting(self):
57-
""" Get LED setting synchronously. """
62+
"""
63+
Get LED setting synchronously. This feature only works on devices, that announce the led feature.
64+
65+
return: LED settings
66+
"""
5867
led_setting = devolo_idl_proto_deviceapi_ledsettings_pb2.LedSettingsGet()
5968
response = self._get("LedSettingsGet")
6069
led_setting.FromString(response.read())
6170
return self._message_to_dict(led_setting)
6271

6372
@_feature("led")
6473
async def async_set_led_setting(self, enable: bool) -> bool:
65-
""" Set LED setting asynchronously. """
74+
"""
75+
Set LED setting asynchronously. This feature only works on devices, that announce the led feature.
76+
77+
:param enable: True to enable the LEDs, False to disable the LEDs
78+
:return: True, if LED state was successfully changed, otherwise False
79+
"""
6680
led_setting = devolo_idl_proto_deviceapi_ledsettings_pb2.LedSettingsSet()
6781
led_setting.state = int(not enable)
6882
query = await self._async_post("LedSettingsSet", data=led_setting.SerializeToString())
@@ -72,33 +86,105 @@ async def async_set_led_setting(self, enable: bool) -> bool:
7286

7387
@_feature("led")
7488
def set_led_setting(self, enable: bool) -> bool:
75-
""" Set LED setting synchronously. """
89+
"""
90+
Set LED setting synchronously. This feature only works on devices, that announce the led feature.
91+
92+
:param enable: True to enable the LEDs, False to disable the LEDs
93+
:return: True, if LED state was successfully changed, otherwise False
94+
"""
7695
led_setting = devolo_idl_proto_deviceapi_ledsettings_pb2.LedSettingsSet()
7796
led_setting.state = int(not enable)
7897
query = self._post("LedSettingsSet", data=led_setting.SerializeToString())
7998
response = devolo_idl_proto_deviceapi_ledsettings_pb2.LedSettingsSetResponse()
8099
response.FromString(query.read())
81100
return bool(not response.result)
82101

102+
103+
@_feature("update")
104+
async def async_check_firmware_available(self) -> dict:
105+
"""
106+
Check asynchronously, if a firmware update is available for the device.
107+
108+
:return: Result and new firmware version, if newer one is available
109+
"""
110+
update_firmware_check = devolo_idl_proto_deviceapi_updatefirmware_pb2.UpdateFirmwareCheck()
111+
response = await self._async_get("UpdateFirmwareCheck")
112+
update_firmware_check.ParseFromString(await response.aread())
113+
return self._message_to_dict(update_firmware_check)
114+
115+
@_feature("update")
116+
def check_firmware_available(self) -> dict:
117+
"""
118+
Check synchronously, if a firmware update is available for the device.
119+
120+
:return: Result and new firmware version, if newer one is available
121+
"""
122+
update_firmware_check = devolo_idl_proto_deviceapi_updatefirmware_pb2.UpdateFirmwareCheck()
123+
response = self._get("UpdateFirmwareCheck")
124+
update_firmware_check.ParseFromString(response.read())
125+
return self._message_to_dict(update_firmware_check)
126+
127+
@_feature("update")
128+
async def async_start_firmware_update(self) -> bool:
129+
"""
130+
Start firmware update asynchronously, if a firmware update is available for the device. Important: The response does
131+
not tell you anything about the success of the update itself.
132+
133+
:return: True, if the firmware update was started, False if there is no update
134+
"""
135+
update_firmware = devolo_idl_proto_deviceapi_updatefirmware_pb2.UpdateFirmwareStart()
136+
response = await self._async_get("UpdateFirmwareStart")
137+
update_firmware.FromString(await response.aread())
138+
return bool(not update_firmware.result)
139+
140+
@_feature("update")
141+
def start_firmware_update(self) -> bool:
142+
"""
143+
Start firmware update synchronously, if a firmware update is available for the device. Important: The response does
144+
not tell you anything about the success of the update itself.
145+
146+
:return: True, if the firmware update was started, False if there is no update
147+
"""
148+
update_firmware = devolo_idl_proto_deviceapi_updatefirmware_pb2.UpdateFirmwareStart()
149+
response = self._get("UpdateFirmwareStart")
150+
update_firmware.FromString(response.read())
151+
return bool(not update_firmware.result)
152+
153+
83154
@_feature("wifi1")
84155
async def async_get_wifi_connected_station(self) -> dict:
85-
""" Get wifi stations connected to the device asynchronously. """
156+
"""
157+
Get wifi stations connected to the device asynchronously. This feature only works on devices, that announce the wifi1
158+
feature.
159+
160+
:return: All connected wifi stations including connection rate data
161+
"""
86162
wifi_connected_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiConnectedStationsGet()
87163
response = await self._async_get("WifiConnectedStationsGet")
88164
wifi_connected_proto.ParseFromString(await response.aread())
89165
return self._message_to_dict(wifi_connected_proto)
90166

91167
@_feature("wifi1")
92168
def get_wifi_connected_station(self) -> dict:
93-
""" Get wifi stations connected to the device synchronously. """
169+
"""
170+
Get wifi stations connected to the device synchronously. This feature only works on devices, that announce the wifi1
171+
feature.
172+
173+
:return: All connected wifi stations including connection rate data
174+
"""
94175
wifi_connected_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiConnectedStationsGet()
95176
response = self._get("WifiConnectedStationsGet")
96177
wifi_connected_proto.ParseFromString(response.read())
97178
return self._message_to_dict(wifi_connected_proto)
98179

99180
@_feature("wifi1")
100181
async def async_get_wifi_guest_access(self) -> dict:
101-
""" Get details about wifi guest access asynchronously. """
182+
"""
183+
Get details about wifi guest access asynchronously. This feature only works on devices, that announce the wifi1
184+
feature.
185+
186+
:return: Details about the wifi guest access
187+
"""
102188
self._logger.debug("Getting wifi guest access")
103189
wifi_guest_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiGuestAccessGet()
104190
response = await self._async_get("WifiGuestAccessGet")
@@ -107,7 +193,12 @@ async def async_get_wifi_guest_access(self) -> dict:
107193

108194
@_feature("wifi1")
109195
def get_wifi_guest_access(self) -> dict:
110-
""" Get details about wifi guest access synchronously. """
196+
"""
197+
Get details about wifi guest access synchronously. This feature only works on devices, that announce the wifi1
198+
feature.
199+
200+
:return: Details about the wifi guest access
201+
"""
111202
self._logger.debug("Getting wifi guest access")
112203
wifi_guest_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiGuestAccessGet()
113204
response = self._get("WifiGuestAccessGet")
@@ -116,7 +207,12 @@ def get_wifi_guest_access(self) -> dict:
116207

117208
@_feature("wifi1")
118209
async def async_set_wifi_guest_access(self, enable: bool) -> bool:
119-
""" Enable wifi guest access asynchronously. """
210+
"""
211+
Enable wifi guest access asynchronously. This feature only works on devices, that announce the wifi1 feature.
212+
213+
:param enable: True to enable, False to disable wifi guest access
214+
:return: True, if the state of the wifi guest access was successfully changed, otherwise False
215+
"""
120216
wifi_guest_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiGuestAccessSet()
121217
wifi_guest_proto.enable = enable
122218
query = await self._async_post("WifiGuestAccessSet", data=wifi_guest_proto.SerializeToString())
@@ -126,7 +222,12 @@ async def async_set_wifi_guest_access(self, enable: bool) -> bool:
126222

127223
@_feature("wifi1")
128224
def set_wifi_guest_access(self, enable: bool) -> bool:
129-
""" Enable wifi guest access synchronously. """
225+
"""
226+
Enable wifi guest access synchronously. This feature only works on devices, that announce the wifi1 feature.
227+
228+
:param enable: True to enable, False to disable wifi guest access
229+
:return: True, if the state of the wifi guest access was successfully changed, otherwise False
230+
"""
130231
wifi_guest_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiGuestAccessSet()
131232
wifi_guest_proto.enable = enable
132233
query = self._post("WifiGuestAccessSet", data=wifi_guest_proto.SerializeToString())
@@ -136,30 +237,76 @@ def set_wifi_guest_access(self, enable: bool) -> bool:
136237

137238
@_feature("wifi1")
138239
async def async_get_wifi_neighbor_access_points(self) -> dict:
139-
""" Get wifi access point in the neighborhood asynchronously. """
240+
"""
241+
Get wifi access point in the neighborhood asynchronously. This feature only works on devices, that announce the wifi1
242+
feature.
243+
244+
:return: Visible access points in the neighborhood including connection rate data
245+
"""
140246
wifi_neighbor_aps = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiNeighborAPsGet()
141247
response = await self._async_get("WifiNeighborAPsGet", timeout=15.0)
142248
wifi_neighbor_aps.ParseFromString(await response.aread())
143249
return self._message_to_dict(wifi_neighbor_aps)
144250

145251
@_feature("wifi1")
146252
def get_wifi_neighbor_access_points(self) -> dict:
147-
""" Get wifi access point in the neighborhood synchronously. """
253+
"""
254+
Get wifi access point in the neighborhood synchronously. This feature only works on devices, that announce the wifi1
255+
feature.
256+
257+
:return: Visible access points in the neighborhood including connection rate data
258+
"""
148259
wifi_neighbor_aps = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiNeighborAPsGet()
149260
response = self._get("WifiNeighborAPsGet", timeout=15.0)
150261
wifi_neighbor_aps.ParseFromString(response.read())
151262
return self._message_to_dict(wifi_neighbor_aps)
152263

153264
@_feature("wifi1")
154265
async def async_get_wifi_repeated_access_points(self):
266+
"""
267+
Get repeated wifi access point asynchronously. This feature only works on repeater devices, that announce the wifi1
268+
feature.
269+
270+
:return: Repeated access points in the neighborhood including connection rate data
271+
"""
155272
wifi_connected_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiRepeatedAPsGet()
156273
response = await self._async_get("WifiRepeatedAPsGet")
157274
wifi_connected_proto.ParseFromString(await response.aread())
158275
return self._message_to_dict(wifi_connected_proto)
159276

160277
@_feature("wifi1")
161278
def get_wifi_repeated_access_points(self):
279+
"""
280+
Get repeated wifi access point synchronously. This feature only works on repeater devices, that announce the wifi1
281+
feature.
282+
283+
:return: Repeated access points in the neighborhood including connection rate data
284+
"""
162285
wifi_connected_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiRepeatedAPsGet()
163286
response = self._get("WifiRepeatedAPsGet")
164287
wifi_connected_proto.ParseFromString(response.read())
165288
return self._message_to_dict(wifi_connected_proto)
289+
290+
@_feature("wifi1")
291+
async def async_start_wps(self):
292+
"""
293+
Start WPS push button configuration.
294+
295+
:return: True, if the WPS was successfully started, otherwise False
296+
"""
297+
wps_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiWpsPbcStart()
298+
response = await self._async_get("WifiWpsPbcStart")
299+
wps_proto.FromString(await response.aread())
300+
return bool(not wps_proto.result)
301+
302+
@_feature("wifi1")
303+
def start_wps(self):
304+
"""
305+
Start WPS push button configuration.
306+
307+
:return: True, if the WPS was successfully started, otherwise False
308+
"""
309+
wps_proto = devolo_idl_proto_deviceapi_wifinetwork_pb2.WifiWpsPbcStart()
310+
response = self._get("WifiWpsPbcStart")
311+
wps_proto.FromString(response.read())
312+
return bool(not wps_proto.result)

0 commit comments

Comments
 (0)