pybeoplay 2.2.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.
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2019 Giovanni Iachello
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Giovanni Iachello
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pybeoplay
3
- Version: 2.2.0
3
+ Version: 2.4.0
4
4
  Summary: BeoPlay API for Python
5
5
  Home-page: https://github.com/giachello/pybeoplay
6
- Download-URL: https://github.com/giachello/pybeoplay/tarball/2.2.0
6
+ Download-URL: https://github.com/giachello/pybeoplay/tarball/2.4.0
7
7
  Author: Giovanni Iachello
8
8
  Author-email: giovanni.iachello@gmail.com
9
9
  License: MIT License
@@ -1,12 +1,13 @@
1
- #
2
- # BeoPlay JSON interface for Bang & Olufsen Speakers, TVs and other NL devices
3
- # Reference: https://documenter.getpostman.com/view/1053298/T1LTe4Lt#intro
4
- # Reference: https://raw.githubusercontent.com/PolyPv/BeoRemote/master/BeoRemote.txt
5
- # Lifted a lot of code from marton borzak's ha-beoplay
6
- #
7
- #
8
- # https://widdowquinn.github.io/coding/update-pypi-package/
9
- #
1
+ """
2
+
3
+ BeoPlay JSON interface for Bang & Olufsen Speakers, TVs and other NL devices
4
+ Reference: https://documenter.getpostman.com/view/1053298/T1LTe4Lt#intro
5
+ Reference: https://raw.githubusercontent.com/PolyPv/BeoRemote/master/BeoRemote.txt
6
+ Lifted a lot of code from marton borzak's ha-beoplay
7
+
8
+ https://widdowquinn.github.io/coding/update-pypi-package/
9
+
10
+ """
10
11
 
11
12
  import requests
12
13
  import aiohttp
@@ -25,7 +26,8 @@ class BeoPlay(object):
25
26
  """Initializes a BeoPlay connection to the speaker / TV
26
27
  Host: the IP address of the speaker
27
28
  Session (optional): a asyncio client session to be used for async
28
- communication with the speaker (if not provided, only blocking calls using Requests will work
29
+ communication with the speaker (if not provided, only blocking calls
30
+ using Requests will work)
29
31
  """
30
32
  # network information
31
33
  self._host = host
@@ -34,6 +36,7 @@ class BeoPlay(object):
34
36
  )
35
37
  self._connfail = 0
36
38
  self._clientsession = session
39
+ # The following are only going ot be valid after a call to getDeviceInfo
37
40
  # device information
38
41
  self._name = None
39
42
  self._serialNumber = None
@@ -42,6 +45,10 @@ class BeoPlay(object):
42
45
  self._typeName = None
43
46
  self._softwareVersion = None
44
47
  self._hardwareVersion = None
48
+ # The following are only going ot be valid after a call to getMediaInfo, getStandby
49
+ # Or, they are updated by the Notifications task. The actual field updated by
50
+ # Notifications varies by device. Some devices for example provide notifications when
51
+ # Sound mode changes (e.g., Stage), others (e.g., BeoVision Avant 55) don't.
45
52
  # State and Media information
46
53
  self.on = None
47
54
  self.min_volume = None
@@ -57,19 +64,27 @@ class BeoPlay(object):
57
64
  self.media_country = None
58
65
  self.media_languages = None
59
66
  self.primary_experience = None
67
+ # The following are only going ot be valid after a call to getSources
60
68
  # Sources
61
69
  self.source = None
62
70
  self.sources = []
63
71
  self.sourcesID = []
64
72
  self.sourcesBorrowed = []
73
+ # The following are only going ot be valid after a call to getSoundModes
65
74
  # Sound modes
66
- self.soundMode = None
75
+ self._soundMode = None
67
76
  self._soundModes = {}
77
+ # The following are only going ot be valid after a call to getStandPosition
68
78
  # Stand control
69
79
  self.standPosition = None
70
80
  self.standPositions = []
71
81
  self.standPositionsID = []
72
82
 
83
+ @property
84
+ def host(self):
85
+ """Return the device host."""
86
+ return self._host
87
+
73
88
  @property
74
89
  def name(self):
75
90
  """Return the device name."""
@@ -115,6 +130,11 @@ class BeoPlay(object):
115
130
  """Get the list of available digits"""
116
131
  return BEOPLAY_DIGITS
117
132
 
133
+ @property
134
+ def soundMode(self):
135
+ """Get the current sound modes"""
136
+ return self._soundMode
137
+
118
138
  @property
119
139
  def soundModes(self):
120
140
  """Get the list of available sound modes"""
@@ -122,7 +142,7 @@ class BeoPlay(object):
122
142
 
123
143
  @property
124
144
  def soundModesID(self):
125
- """Get the list of available sound modes"""
145
+ """Get the list of available sound mode IDs"""
126
146
  return list(self._soundModes.values())
127
147
 
128
148
  ###############################################################
@@ -231,9 +251,16 @@ class BeoPlay(object):
231
251
  # GET ATTRIBUTES FROM THE SPEAKER - NON-BLOCKING CALLS
232
252
  ###############################################################
233
253
 
254
+ async def async_get_source(self):
255
+ self.source = None
256
+ r = await self.async_getReq(BEOPLAY_URL_ACTIVE_SOURCES)
257
+ if r:
258
+ self.source = r["primaryExperience"]["source"]["friendlyName"] if "friendlyName" in r["primaryExperience"]["source"] else None
259
+ return self.source
260
+
234
261
  # edited to only include in Use sources
235
262
  async def async_get_sources(self):
236
- r = await self.async_getReq("BeoZone/Zone/Sources")
263
+ r = await self.async_getReq(BEOPLAY_URL_GET_SOURCES)
237
264
  if r:
238
265
  # clear previously stored sources
239
266
  self.sources = []
@@ -252,7 +279,7 @@ class BeoPlay(object):
252
279
 
253
280
  async def async_get_standby(self) -> bool:
254
281
  """Returns True of the device is on, False if off."""
255
- r = await self.async_getReq("BeoDevice/powerManagement/standby")
282
+ r = await self.async_getReq(BEOPLAY_URL_STANDBY)
256
283
  if r:
257
284
  if r["standby"]["powerState"] == "on":
258
285
  self.on = True
@@ -262,25 +289,29 @@ class BeoPlay(object):
262
289
  return
263
290
 
264
291
  async def async_get_sound_mode(self):
265
- r = await self.async_getReq(BEOPLAY_URL_GET_SOUND_MODE)
266
- if r:
267
- self.soundMode = r["mode"]["active"]
268
- return self.soundMode
269
- return
292
+ """Returns the current sound mode, or None if not retrieved."""
293
+ self._soundMode = None
294
+ await self.async_get_sound_modes()
295
+ return self._soundMode
270
296
 
271
297
  async def async_get_sound_modes(self):
298
+ """Returns a dictionary of available sound modes, or None if not retrieved."""
272
299
  r = await self.async_getReq(BEOPLAY_URL_GET_SOUND_MODE)
273
300
  if r:
274
301
  r = r.get("mode", {"list": []})
275
- r = r.get("list", [])
276
- for element in r:
302
+ l = r.get("list", [])
303
+ a = r.get("active", None)
304
+ for element in l:
277
305
  self._soundModes[element["friendlyName"]] = element["id"]
306
+ if a and a == element["id"]:
307
+ self._soundMode = element["friendlyName"]
278
308
  return self._soundModes
279
309
  return
280
310
 
281
311
  async def async_get_stand_position(self):
282
312
  """Returns the stand position, or None if not retrieved."""
283
- r = await self.async_getReq("BeoZone/Zone/Stand/Active")
313
+ self._standPosition = None
314
+ r = await self.async_getReq(BEOPLAY_URL_STAND_ACTIVE)
284
315
  if r:
285
316
  if r["active"] is not None:
286
317
  self._standPosition = r["active"]
@@ -288,11 +319,11 @@ class BeoPlay(object):
288
319
  return
289
320
 
290
321
  async def async_get_stand_positions(self):
291
- r = await self.async_getReq("BeoZone/Zone/Stand")
322
+ # clear previous stand positions
323
+ self.standPositions = []
324
+ self.standPositionsID = []
325
+ r = await self.async_getReq(BEOPLAY_URL_STAND)
292
326
  if r:
293
- # clear previous stand positions
294
- self.standPositions = []
295
- self.standPositionsID = []
296
327
  for elements in r:
297
328
  i = 0
298
329
  while i < len(r[elements]):
@@ -378,7 +409,7 @@ class BeoPlay(object):
378
409
  chosenSource = self.sourcesID[i]
379
410
  await self.async_postReq(
380
411
  "POST",
381
- BEOPLAY_URL_SET_SOURCE,
412
+ BEOPLAY_URL_ACTIVE_SOURCES,
382
413
  {"primaryExperience": {"source": {"id": chosenSource}}},
383
414
  )
384
415
  i += 1
@@ -402,7 +433,7 @@ class BeoPlay(object):
402
433
  if self.standPositions[i] == standPosition:
403
434
  chosenStandPosition = self.standPositionsID[i]
404
435
  await self.async_postReq(
405
- "PUT", BEOPLAY_URL_SET_STAND, {"active": chosenStandPosition}
436
+ "PUT", BEOPLAY_URL_STAND_ACTIVE, {"active": chosenStandPosition}
406
437
  )
407
438
  i += 1
408
439
 
@@ -549,7 +580,7 @@ class BeoPlay(object):
549
580
 
550
581
  # edited to only include in Use sources
551
582
  def getSources(self):
552
- r = self._getReq("BeoZone/Zone/Sources")
583
+ r = self._getReq(BEOPLAY_URL_GET_SOURCES)
553
584
  if r:
554
585
  for elements in r:
555
586
  i = 0
@@ -560,8 +591,15 @@ class BeoPlay(object):
560
591
  self.sourcesID.append(r[elements][i][0])
561
592
  i += 1
562
593
 
594
+ def getSource(self):
595
+ self.source = None
596
+ r = self._getReq(BEOPLAY_URL_ACTIVE_SOURCES)
597
+ if r:
598
+ self.source = r["primaryExperience"]["source"]["friendlyName"] if "friendlyName" in r["primaryExperience"]["source"] else None
599
+ return self.source
600
+
563
601
  def getStandby(self):
564
- r = self._getReq("BeoDevice/powerManagement/standby")
602
+ r = self._getReq(BEOPLAY_URL_STANDBY)
565
603
  if r:
566
604
  if r["standby"]["powerState"] == "on":
567
605
  self.on = True
@@ -569,26 +607,38 @@ class BeoPlay(object):
569
607
  self.on = False
570
608
 
571
609
  def getSoundMode(self):
572
- r = self._getReq(BEOPLAY_URL_GET_SOUND_MODE)
573
- if r:
574
- self.soundMode = r["mode"]["active"]
610
+ """ Get sound mode. Return the current active sound mode or None if not retreived."""
611
+ self._soundMode = None
612
+ self.getSoundModes()
613
+ return self._soundMode
575
614
 
576
615
  def getSoundModes(self):
616
+ """ Get sound modes. You need to call this before reading soundMode or soundModes."""
577
617
  r = self._getReq(BEOPLAY_URL_GET_SOUND_MODE)
578
618
  if r:
579
619
  r = r.get("mode", {"list": []})
580
- r = r.get("list", [])
581
- for element in r:
620
+ l = r.get("list", [])
621
+ a = r.get("active", None)
622
+ for element in l:
582
623
  self._soundModes[element["friendlyName"]] = element["id"]
624
+ if a and a == element["id"]:
625
+ self._soundMode = element["friendlyName"]
626
+ return self._soundModes
627
+ return None
583
628
 
584
629
  def getStandPosition(self):
585
- r = self._getReq("BeoZone/Zone/Stand/Active")
630
+ self._standPosition = None
631
+ r = self._getReq(BEOPLAY_URL_STAND_ACTIVE)
586
632
  if r:
587
633
  if r["active"] is not None:
588
- self._standPosition = r["active"]
634
+ self._standPosition = r["active"]
635
+ return self._standPosition
636
+ return None
589
637
 
590
638
  def getStandPositions(self):
591
- r = self._getReq("BeoZone/Zone/Stand")
639
+ self.standPositions = []
640
+ self.standPositionsID = []
641
+ r = self._getReq(BEOPLAY_URL_STAND)
592
642
  if r:
593
643
  for elements in r:
594
644
  i = 0
@@ -596,6 +646,7 @@ class BeoPlay(object):
596
646
  self.standPositions.append(r[elements][i][1]["friendlyName"])
597
647
  self.standPositionsID.append(r[elements][i][0])
598
648
  i += 1
649
+ return self.standPositions
599
650
 
600
651
  def getDeviceInfo(self):
601
652
  r = self._getReq("BeoDevice")
@@ -669,7 +720,7 @@ class BeoPlay(object):
669
720
  chosenSource = self.sourcesID[i]
670
721
  self._postReq(
671
722
  "POST",
672
- BEOPLAY_URL_SET_SOURCE,
723
+ BEOPLAY_URL_ACTIVE_SOURCES,
673
724
  {"primaryExperience": {"source": {"id": chosenSource}}},
674
725
  )
675
726
  i += 1
@@ -692,7 +743,7 @@ class BeoPlay(object):
692
743
  if self.standPositions[i] == standPosition:
693
744
  chosenStandPosition = self.standPositionsID[i]
694
745
  self._postReq(
695
- "PUT", BEOPLAY_URL_SET_STAND, {"active": chosenStandPosition}
746
+ "PUT", BEOPLAY_URL_STAND_ACTIVE, {"active": chosenStandPosition}
696
747
  )
697
748
  i += 1
698
749
 
@@ -849,6 +900,11 @@ class BeoPlay(object):
849
900
  + data["notification"]["data"]["name"]
850
901
  )
851
902
 
903
+ def _processSoundMode(self, data):
904
+ if data["notification"]["type"] == "SOUND_ACTIVE_MODE_CHANGED":
905
+ self.soundMode = data["notification"]["data"]["friendlyName"]
906
+
907
+
852
908
  def _processNotification(self, data):
853
909
  """Cumulative process all the potential notification information."""
854
910
  try:
@@ -860,5 +916,7 @@ class BeoPlay(object):
860
916
  self._processState(data)
861
917
  # get currently playing music info
862
918
  self._processMusicInfo(data)
919
+ # get sound mode
920
+ self._processSoundMode(data)
863
921
  except KeyError:
864
922
  LOG.debug("Malformed notification: %s", str(data))
@@ -5,16 +5,15 @@
5
5
  # Lifted a lot of code from marton borzak's ha-beoplay
6
6
  #
7
7
  #
8
- # pybeoplay constants
9
- #
10
-
11
8
 
9
+ # Connection constants
12
10
  BASE_URL = 'http://{0}:8080/{1}'
13
11
  TIMEOUT = 5.0
14
12
  CONNFAILCOUNT = 5
15
13
 
16
-
14
+ # BeoPlay constants
17
15
  BEOPLAY_URL_NOTIFICATIONS = 'BeoNotify/Notifications'
16
+
18
17
  BEOPLAY_URL_SET_VOLUME = 'BeoZone/Zone/Sound/Volume/Speaker/Level'
19
18
  BEOPLAY_URL_MUTE = 'BeoZone/Zone/Sound/Volume/Speaker/Muted'
20
19
  BEOPLAY_URL_PLAY = 'BeoZone/Zone/Stream/Play'
@@ -28,10 +27,15 @@ BEOPLAY_URL_STEPDOWN = 'BeoZone/Zone/List/StepDown'
28
27
  BEOPLAY_URL_SHUFFLE = 'BeoZone/Zone/List/Shuffle'
29
28
  BEOPLAY_URL_REPEAT = 'BeoZone/Zone/List/Repeat'
30
29
  BEOPLAY_URL_STANDBY = 'BeoDevice/powerManagement/standby'
31
- BEOPLAY_URL_SET_SOURCE = 'BeoZone/Zone/ActiveSources'
32
- BEOPLAY_URL_SET_STAND = 'BeoZone/Zone/Stand/Active'
30
+
31
+ BEOPLAY_URL_GET_SOURCES = 'BeoZone/Zone/Sources'
32
+ BEOPLAY_URL_ACTIVE_SOURCES = 'BeoZone/Zone/ActiveSources'
33
+
34
+ BEOPLAY_URL_STAND_ACTIVE = 'BeoZone/Zone/Stand/Active'
35
+ BEOPLAY_URL_STAND = 'BeoZone/Zone/Stand'
33
36
  BEOPLAY_URL_GET_SOUND_MODE = 'BeoZone/Zone/Sound/Mode'
34
37
  BEOPLAY_URL_SET_SOUND_MODE = 'BeoZone/Zone/Sound/Mode/Active'
38
+
35
39
  BEOPLAY_URL_JOIN_EXPERIENCE = 'BeoZone/Zone/Device/OneWayJoin'
36
40
  BEOPLAY_URL_LEAVE_EXPERIENCE = 'BeoZone/Zone/ActiveSources/primaryExperience'
37
41
  BEOPLAY_URL_PLAYQUEUE = 'BeoZone/Zone/PlayQueue'
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pybeoplay
3
- Version: 2.2.0
3
+ Version: 2.4.0
4
4
  Summary: BeoPlay API for Python
5
5
  Home-page: https://github.com/giachello/pybeoplay
6
- Download-URL: https://github.com/giachello/pybeoplay/tarball/2.2.0
6
+ Download-URL: https://github.com/giachello/pybeoplay/tarball/2.4.0
7
7
  Author: Giovanni Iachello
8
8
  Author-email: giovanni.iachello@gmail.com
9
9
  License: MIT License
@@ -1,43 +1,43 @@
1
- import os
2
- from setuptools import setup, find_packages
3
-
4
- def readme():
5
- with open('README.md') as f:
6
- return f.read()
7
-
8
- PACKAGE_NAME = 'pybeoplay'
9
- HERE = os.path.abspath(os.path.dirname(__file__))
10
- VERSION = '2.2.0'
11
-
12
- PACKAGES = find_packages(exclude=['tests', 'tests.*', 'dist', 'ccu', 'build'])
13
-
14
- REQUIRES = []
15
-
16
- long_description = readme()
17
-
18
-
19
- setup(
20
- name=PACKAGE_NAME,
21
- version=VERSION,
22
- license='MIT License',
23
- url='https://github.com/giachello/pybeoplay',
24
- download_url='https://github.com/giachello/pybeoplay/tarball/'+VERSION,
25
- author='Giovanni Iachello',
26
- author_email='giovanni.iachello@gmail.com',
27
- description='BeoPlay API for Python',
28
- long_description=long_description,
29
- long_description_content_type='text/markdown',
30
- readme="README.md",
31
- packages=PACKAGES,
32
- include_package_data=True,
33
- zip_safe=False,
34
- platforms='any',
35
- install_requires=REQUIRES,
36
- keywords=['beoplay', 'pybeoplay', 'B&O' , 'Bang & Olufsen'],
37
- classifiers=[
38
- 'Intended Audience :: Developers',
39
- 'License :: OSI Approved :: MIT License',
40
- 'Operating System :: OS Independent',
41
- 'Programming Language :: Python :: 3.9'
42
- ],
43
- )
1
+ import os
2
+ from setuptools import setup, find_packages
3
+
4
+ def readme():
5
+ with open('README.md') as f:
6
+ return f.read()
7
+
8
+ PACKAGE_NAME = 'pybeoplay'
9
+ HERE = os.path.abspath(os.path.dirname(__file__))
10
+ VERSION = '2.4.0'
11
+
12
+ PACKAGES = find_packages(exclude=['tests', 'tests.*', 'dist', 'ccu', 'build'])
13
+
14
+ REQUIRES = []
15
+
16
+ long_description = readme()
17
+
18
+
19
+ setup(
20
+ name=PACKAGE_NAME,
21
+ version=VERSION,
22
+ license='MIT License',
23
+ url='https://github.com/giachello/pybeoplay',
24
+ download_url='https://github.com/giachello/pybeoplay/tarball/'+VERSION,
25
+ author='Giovanni Iachello',
26
+ author_email='giovanni.iachello@gmail.com',
27
+ description='BeoPlay API for Python',
28
+ long_description=long_description,
29
+ long_description_content_type='text/markdown',
30
+ readme="README.md",
31
+ packages=PACKAGES,
32
+ include_package_data=True,
33
+ zip_safe=False,
34
+ platforms='any',
35
+ install_requires=REQUIRES,
36
+ keywords=['beoplay', 'pybeoplay', 'B&O' , 'Bang & Olufsen'],
37
+ classifiers=[
38
+ 'Intended Audience :: Developers',
39
+ 'License :: OSI Approved :: MIT License',
40
+ 'Operating System :: OS Independent',
41
+ 'Programming Language :: Python :: 3.9'
42
+ ],
43
+ )
File without changes
File without changes