Skip to content

Commit ebb0e95

Browse files
committed
Prepare 0.31.0 release
1 parent 6404254 commit ebb0e95

File tree

6 files changed

+78
-29
lines changed

6 files changed

+78
-29
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
## [Unreleased]
66
- No unreleased changes so far
77

8+
## [0.31.0] - 2022-01-14
9+
### Added
10+
- Image support is now optional and can be installed with pip install weconnect\[Images\], remove \[Images\] if you do not need image support in your project
11+
- New disconnect method that cancles all timers and thus terminates all additional threads
12+
- New timeout parameter to limit the time waiting in requests for the VW servers
13+
814
## [0.30.4] - 2022-01-12
915
### Fixed
1016
- Fix problem with stored tokens

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Python API for the Volkswagen WeConnect Services. If you are not a developer and
1717
- [WeConnect-cli](https://github.com/tillsteinbach/WeConnect-cli): A commandline interface to interact with WeConnect
1818
- [WeConnect-MQTT](https://github.com/tillsteinbach/WeConnect-mqtt): A MQTT Client that provides WeConnect data to the MQTT Broker of your choice (e.g. your home automation solution such as [ioBroker](https://www.iobroker.net), [FHEM](https://fhem.de) or [Home Assistant](https://www.home-assistant.io))
1919

20+
## Install
21+
´´´
22+
pip3 install weconnect[Images]
23+
´´´
24+
2025
## Getting started
2126
- To get started have a look in the [examples folder](https://github.com/tillsteinbach/WeConnect-python/tree/main/examples)
2227

weconnect/addressable.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,21 @@
55
from datetime import datetime, timezone
66
from enum import Enum, IntEnum, Flag, auto
77

8-
from PIL import Image # type: ignore
9-
import ascii_magic # type: ignore
10-
118
from weconnect.util import toBool, imgToASCIIArt
129

10+
SUPPORT_IMAGES = False
11+
try:
12+
from PIL import Image # type: ignore
13+
SUPPORT_IMAGES = True
14+
except ImportError:
15+
pass
16+
17+
SUPPORT_ASCII_IMAGES = False
18+
try:
19+
import ascii_magic # type: ignore
20+
SUPPORT_ASCII_IMAGES = True
21+
except ImportError:
22+
pass
1323

1424
LOG: logging.Logger = logging.getLogger("weconnect")
1525

@@ -229,13 +239,13 @@ def saveToFile(self, filename: str) -> None: # noqa: C901
229239
if self.value is not None:
230240
if filename.endswith(('.txt', '.TXT', '.text')):
231241
with open(filename, mode='w', encoding='utf8') as textfile:
232-
if isinstance(self.value, Image.Image):
242+
if SUPPORT_IMAGES and SUPPORT_ASCII_IMAGES and isinstance(self.value, Image.Image):
233243
textfile.write(imgToASCIIArt(self.value, columns=120, mode=ascii_magic.Modes.ASCII))
234244
else:
235245
textfile.write(str(self))
236246
elif filename.endswith(('.htm', '.HTM', '.html', '.HTML')):
237247
with open(filename, mode='w', encoding='utf8') as htmlfile:
238-
if isinstance(self.value, Image.Image):
248+
if SUPPORT_IMAGES and SUPPORT_ASCII_IMAGES and isinstance(self.value, Image.Image):
239249
html = """<!DOCTYPE html><head><title>ASCII art</title></head><body><pre style="display: inline-block; border-width: 4px 6px;
240250
border-color: black; border-style: solid; background-color:black; font-size: 8px;">"""
241251
htmlfile.write(html)
@@ -245,13 +255,13 @@ def saveToFile(self, filename: str) -> None: # noqa: C901
245255
htmlfile.write(str(self))
246256
elif filename.endswith(('.png', '.PNG')):
247257
with open(filename, mode='wb') as pngfile:
248-
if isinstance(self.value, Image.Image):
258+
if SUPPORT_IMAGES and isinstance(self.value, Image.Image):
249259
self.value.save(fp=pngfile, format='PNG') # pylint: disable=no-member
250260
else:
251261
raise ValueError('Attribute is no image and cannot be converted to one')
252262
elif filename.endswith(('.jpg', '.JPG', '.jpeg', '.JPEG')):
253263
with open(filename, mode='wb') as jpgfile:
254-
if isinstance(self.value, Image.Image):
264+
if SUPPORT_IMAGES and isinstance(self.value, Image.Image):
255265
if self.value.mode in ("RGBA", "P"): # pylint: disable=no-member
256266
raise ValueError('Image contains transparency and thus cannot be saved as jpeg-file')
257267
self.value.save(fp=jpgfile, format='JPEG') # pylint: disable=no-member
@@ -267,7 +277,7 @@ def __str__(self) -> str:
267277
return str(self.value.value) # pylint: disable=no-member
268278
if isinstance(self.value, datetime):
269279
return self.value.isoformat() # pylint: disable=no-member
270-
if isinstance(self.value, Image.Image):
280+
if SUPPORT_IMAGES and isinstance(self.value, Image.Image):
271281
return imgToASCIIArt(self.value) # pylint: disable=no-member
272282
return str(self.value)
273283

weconnect/elements/controls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def __onClimatizationControlChange(self, element, flags): # noqa: C901
6767
settingsDict['targetTemperature_K'] = celsiusToKelvin(20.5)
6868

6969
data = json.dumps(settingsDict)
70-
controlResponse = self.vehicle.weConnect.session.post(url, data=data, allow_redirects=True)
70+
controlResponse = self.vehicle.weConnect.session.post(url, data=data, allow_redirects=True, timeout=self.vehicle.weConnect.timeout)
7171
if controlResponse.status_code != requests.codes['ok']:
7272
errorDict = controlResponse.json()
7373
if errorDict is not None and 'error' in errorDict:
@@ -97,7 +97,7 @@ def __onChargingControlChange(self, element, flags): # noqa: C901
9797
if element.value in [ControlOperation.START, ControlOperation.STOP]:
9898
url = f'https://mobileapi.apps.emea.vwapps.io/vehicles/{self.vehicle.vin.value}/charging/{element.value.value}'
9999

100-
controlResponse = self.vehicle.weConnect.session.post(url, data='{}', allow_redirects=True)
100+
controlResponse = self.vehicle.weConnect.session.post(url, data='{}', allow_redirects=True, timeout=self.vehicle.weConnect.timeout)
101101
if controlResponse.status_code != requests.codes['ok']:
102102
errorDict = controlResponse.json()
103103
if errorDict is not None and 'error' in errorDict:

weconnect/elements/vehicle.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from weconnect.elements.generic_status import GenericStatus
1212

1313
from requests import exceptions, codes
14-
from PIL import Image # type: ignore
1514

1615
from weconnect.addressable import AddressableObject, AddressableAttribute, AddressableDict, AddressableList
1716
if TYPE_CHECKING:
@@ -47,6 +46,13 @@
4746

4847
from weconnect.elements.helpers.request_tracker import RequestTracker
4948

49+
SUPPORT_IMAGES = False
50+
try:
51+
from PIL import Image # type: ignore
52+
SUPPORT_IMAGES = True
53+
except ImportError:
54+
pass
55+
5056
LOG: logging.Logger = logging.getLogger("weconnect")
5157

5258

@@ -86,9 +92,10 @@ def __init__(
8692
self.controls: Controls = Controls(localAddress='controls', vehicle=self, parent=self)
8793
self.fixAPI: bool = fixAPI
8894

89-
self.__carImages: Dict[str, Image.Image] = {}
90-
self.__badges: Dict[Vehicle.Badge, Image.Image] = {}
91-
self.pictures: AddressableDict[str, Image.Image] = AddressableDict(localAddress='pictures', parent=self)
95+
if SUPPORT_IMAGES:
96+
self.__carImages: Dict[str, Image.Image] = {}
97+
self.__badges: Dict[Vehicle.Badge, Image.Image] = {}
98+
self.pictures: AddressableDict[str, Image.Image] = AddressableDict(localAddress='pictures', parent=self)
9299

93100
self.requestTracker: Optional[RequestTracker] = None
94101
if enableTracker:
@@ -227,7 +234,7 @@ def update( # noqa: C901 # pylint: disable=too-many-branches
227234
LOG.warning('%s: Unknown attribute %s with value %s', self.getGlobalAddress(), key, value)
228235

229236
self.updateStatus(updateCapabilities=updateCapabilities, force=force, selective=selective)
230-
if updatePictures:
237+
if SUPPORT_IMAGES and updatePictures:
231238
for badge in Vehicle.Badge:
232239
badgeImg: Image = Image.open(f'{os.path.dirname(__file__)}/../badges/{badge.value}.png')
233240
badgeImg.thumbnail((100, 100))
@@ -361,6 +368,8 @@ def updateStatus(self, updateCapabilities: bool = True, force: bool = False, #
361368
parkingPosition.enabled = False
362369

363370
def updatePictures(self) -> None: # noqa: C901
371+
if not SUPPORT_IMAGES:
372+
return
364373
url: str = f'https://vehicle-images-service.apps.emea.vwapps.io/v2/vehicle-images/{self.vin.value}?resolution=2x'
365374
data = self.weConnect.fetchData(url, allowHttpError=True)
366375
if data is not None and 'data' in data: # pylint: disable=too-many-nested-blocks
@@ -376,7 +385,7 @@ def updatePictures(self) -> None: # noqa: C901
376385
if img is None or self.weConnect.maxAgePictures is None \
377386
or (cacheDate is not None and cacheDate < (datetime.utcnow() - timedelta(seconds=self.weConnect.maxAgePictures))):
378387
try:
379-
imageDownloadResponse = self.weConnect.session.get(imageurl, stream=True)
388+
imageDownloadResponse = self.weConnect.session.get(imageurl, stream=True, timeout=self.weConnect.timeout)
380389
self.weConnect.recordElapsed(imageDownloadResponse.elapsed)
381390
if imageDownloadResponse.status_code == codes['ok']:
382391
img = Image.open(imageDownloadResponse.raw)
@@ -388,7 +397,7 @@ def updatePictures(self) -> None: # noqa: C901
388397
elif imageDownloadResponse.status_code == codes['unauthorized']:
389398
LOG.info('Server asks for new authorization')
390399
self.weConnect.login()
391-
imageDownloadResponse = self.weConnect.session.get(imageurl, stream=True)
400+
imageDownloadResponse = self.weConnect.session.get(imageurl, stream=True, timeout=self.weConnect.timeout)
392401
self.weConnect.recordElapsed(imageDownloadResponse.elapsed)
393402
if imageDownloadResponse.status_code == codes['ok']:
394403
img = Image.open(imageDownloadResponse.raw)
@@ -434,6 +443,8 @@ def updatePictures(self) -> None: # noqa: C901
434443
self.updateStatusPicture()
435444

436445
def updateStatusPicture(self) -> None: # noqa: C901
446+
if not SUPPORT_IMAGES:
447+
return
437448
if 'car_birdview' in self.__carImages:
438449
img: Image = self.__carImages['car_birdview']
439450

0 commit comments

Comments
 (0)