Repository: bimmerconnected/bimmer_connected
Pull request #263 (2021-01-12 09:03:56)

0.7.14
## Proposed change
<!--
  Describe the big picture of your changes here to communicate to the
  maintainers why we should accept this pull request. If it fixes a bug
  or resolves a feature request, be sure to link to that issue in the
  additional information section.
-->
* Add service attributes
* Fix vehicle postition state
* Fix getting item state

## Type of change
- [x] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (which adds functionality to this library)
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [x] Code quality improvements to existing code or addition of tests

## Additional information
<!--
  Details are important, and help maintainers processing your PR.
  Please be sure to fill out additional details, if applicable.
-->

- This PR fixes or closes issue: fixes #
- This PR is related to issue: 

## Checklist
<!--
  Put an `x` in the boxes that apply. You can also fill these out after
  creating the PR. If you're unsure about any of them, don't hesitate to ask.
  We're here to help! This is simply a reminder of what we are going to look
  for before merging your code.
-->

- [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works.



Patch:
diff --git a/bimmer_connected/all_trips.py b/bimmer_connected/all_trips.py
index 10fded9..ed262e2 100644
--- a/bimmer_connected/all_trips.py
+++ b/bimmer_connected/all_trips.py
@@ -1,6 +1,7 @@
 """Models the all trips of a vehicle."""
 
 import logging
+from typing import List
 
 from bimmer_connected.const import SERVICE_ALL_TRIPS
 
@@ -101,6 +102,15 @@ class AllTrips:  # pylint: disable=too-many-public-methods
         """Generic get function for all backend attributes."""
         return self._state.attributes[SERVICE_ALL_TRIPS][item]
 
+    @property
+    def available_attributes(self) -> List[str]:
+        """Get the list of all-trips attributes available for this vehicle."""
+        result = ['average_combined_consumption', 'average_electric_consumption',
+                  'average_recuperation', 'battery_size_max', 'chargecycle_range',
+                  'reset_date', 'saved_co2', 'saved_co2_green_energy',
+                  'total_electric_distance', 'total_saved_fuel']
+        return result
+
     @property
     @backend_parameter
     def reset_date(self) -> str:
@@ -139,8 +149,8 @@ class AllTrips:  # pylint: disable=too-many-public-methods
 
     @property
     @backend_parameter
-    def average_recopuration(self) -> StatisticValues:
-        """Returns the average recopuration."""
+    def average_recuperation(self) -> StatisticValues:
+        """Returns the average recuperation."""
         return StatisticValues(self._state.attributes[SERVICE_ALL_TRIPS]['avgRecuperation'])
 
     @property
diff --git a/bimmer_connected/charging_profile.py b/bimmer_connected/charging_profile.py
index e3dcbaf..788a8a9 100644
--- a/bimmer_connected/charging_profile.py
+++ b/bimmer_connected/charging_profile.py
@@ -109,6 +109,13 @@ class ChargingProfile:  # pylint: disable=too-many-public-methods
         """Generic get function for all backend attributes."""
         return self._state.attributes[SERVICE_CHARGING_PROFILE][item]
 
+    @property
+    def available_attributes(self) -> List[str]:
+        """Get the list of charging-profile attributes available for this vehicle."""
+        result = ['is_pre_entry_climatization_enabled', 'pre_entry_climatization_timer',
+                  'preferred_charging_window', 'charging_preferences', 'charging_mode']
+        return result
+
     @property
     @backend_parameter
     def is_pre_entry_climatization_enabled(self) -> bool:
@@ -129,8 +136,8 @@ class ChargingProfile:  # pylint: disable=too-many-public-methods
 
     @property
     @backend_parameter
-    def prefered_charging_window(self) -> ChargingWindow:
-        """Returns the prefered charging window."""
+    def preferred_charging_window(self) -> ChargingWindow:
+        """Returns the preferred charging window."""
         return ChargingWindow(self._state.attributes[SERVICE_CHARGING_PROFILE]['preferredChargingWindow'])
 
     @property
diff --git a/bimmer_connected/last_destinations.py b/bimmer_connected/last_destinations.py
index b689d93..be0d188 100644
--- a/bimmer_connected/last_destinations.py
+++ b/bimmer_connected/last_destinations.py
@@ -91,6 +91,12 @@ class LastDestinations:  # pylint: disable=too-many-public-methods
         """
         return self._state.attributes[SERVICE_DESTINATIONS]
 
+    @property
+    def available_attributes(self) -> List[str]:
+        """Get the list of last-destination attributes available for this vehicle."""
+        result = ['last_destinations']
+        return result
+
     def __getattr__(self, item):
         """Generic get function for all backend attributes."""
         return self._state.attributes[SERVICE_DESTINATIONS][item]
diff --git a/bimmer_connected/last_trip.py b/bimmer_connected/last_trip.py
index eb41cd6..302400a 100644
--- a/bimmer_connected/last_trip.py
+++ b/bimmer_connected/last_trip.py
@@ -1,6 +1,7 @@
 """Models the last trip of a vehicle."""
 
 import logging
+from typing import List
 
 from bimmer_connected.const import SERVICE_LAST_TRIP
 
@@ -34,7 +35,7 @@ class LastTrip:  # pylint: disable=too-many-public-methods
     @property
     @backend_parameter
     def attributes(self) -> dict:
-        """Retrieve all attributes from the sever.
+        """Retrieve all attributes from the server.
 
         This does not parse the results in any way.
         """
@@ -44,6 +45,15 @@ class LastTrip:  # pylint: disable=too-many-public-methods
         """Generic get function for all backend attributes."""
         return self._state.attributes[SERVICE_LAST_TRIP][item]
 
+    @property
+    def available_attributes(self) -> List[str]:
+        """Get the list of last-trip attributes available for this vehicle."""
+        result = ['acceleration_value', 'anticipation_value', 'auxiliary_consumption_value',
+                  'average_combined_consumption', 'average_electric_consumption', 'average_recuperation',
+                  'date', 'driving_mode_value', 'duration', 'efficiency_value', 'electric_distance',
+                  'electric_distance_ratio', 'saved_fuel', 'total_consumption_value', 'total_distance']
+        return result
+
     @property
     @backend_parameter
     def efficiency_value(self) -> float:
diff --git a/bimmer_connected/state.py b/bimmer_connected/state.py
index eb3fdcf..0d1bb3a 100644
--- a/bimmer_connected/state.py
+++ b/bimmer_connected/state.py
@@ -122,7 +122,7 @@ class VehicleState:
 
     def __getattr__(self, item):
         """Generic get function for all backend attributes."""
-        return self.vehicle_status.attributes[item]
+        return self.vehicle_status.attributes.get(item)
 
     @staticmethod
     def _parse_datetime(date_str: str) -> datetime.datetime:
diff --git a/bimmer_connected/vehicle_status.py b/bimmer_connected/vehicle_status.py
index fae550c..68ab3e2 100644
--- a/bimmer_connected/vehicle_status.py
+++ b/bimmer_connected/vehicle_status.py
@@ -162,7 +162,7 @@ class VehicleStatus:  # pylint: disable=too-many-public-methods
 
         The server return "OK" if tracking is enabled and "DRIVER_DISABLED" if it is disabled in the vehicle.
         """
-        return self._state.attributes[SERVICE_STATUS]['position']['status'] == 'OK'
+        return self._state.attributes[SERVICE_STATUS]['position']['status'] not in ['DRIVER_DISABLED', 'TOO_FAR_AWAY']
 
     @property
     @backend_parameter


Test patch:
diff --git a/test/test_all_trips.py b/test/test_all_trips.py
index ff0f7d6..5fccc10 100644
--- a/test/test_all_trips.py
+++ b/test/test_all_trips.py
@@ -30,10 +30,10 @@ class TestState(unittest.TestCase):
         self.assertEqual(35.53, state.all_trips.average_electric_consumption.community_high)
         self.assertEqual(14.76, state.all_trips.average_electric_consumption.user_average)
 
-        self.assertEqual(0, state.all_trips.average_recopuration.community_low)
-        self.assertEqual(3.76, state.all_trips.average_recopuration.community_average)
-        self.assertEqual(14.03, state.all_trips.average_recopuration.community_high)
-        self.assertEqual(2.3, state.all_trips.average_recopuration.user_average)
+        self.assertEqual(0, state.all_trips.average_recuperation.community_low)
+        self.assertEqual(3.76, state.all_trips.average_recuperation.community_average)
+        self.assertEqual(14.03, state.all_trips.average_recuperation.community_high)
+        self.assertEqual(2.3, state.all_trips.average_recuperation.user_average)
 
         self.assertEqual(121.58, state.all_trips.chargecycle_range.community_average)
         self.assertEqual(200, state.all_trips.chargecycle_range.community_high)
@@ -50,3 +50,14 @@ class TestState(unittest.TestCase):
         self.assertEqual(1.21, state.all_trips.average_combined_consumption.community_average)
         self.assertEqual(6.2, state.all_trips.average_combined_consumption.community_high)
         self.assertEqual(0.36, state.all_trips.average_combined_consumption.user_average)
+
+    def test_available_attributes(self):
+        """Check available_attributes for all_trips service."""
+        account = mock.MagicMock(ConnectedDriveAccount)
+        state = VehicleState(account, None)
+        expected_attributes = ['average_combined_consumption', 'average_electric_consumption',
+                               'average_recuperation', 'battery_size_max', 'chargecycle_range',
+                               'reset_date', 'saved_co2', 'saved_co2_green_energy',
+                               'total_electric_distance', 'total_saved_fuel']
+        existing_attributes = state.all_trips.available_attributes
+        self.assertListEqual(existing_attributes, expected_attributes)
diff --git a/test/test_charging_profile.py b/test/test_charging_profile.py
index 5f9dc20..a46b96f 100644
--- a/test/test_charging_profile.py
+++ b/test/test_charging_profile.py
@@ -29,5 +29,14 @@ class TestState(unittest.TestCase):
                          state.charging_profile.pre_entry_climatization_timer[TimerTypes.TIMER_1].departure_time)
         self.assertEqual('MONDAY', state.charging_profile.pre_entry_climatization_timer[TimerTypes.TIMER_1].weekdays[0])
 
-        self.assertEqual('05:02', state.charging_profile.prefered_charging_window.start_time)
-        self.assertEqual('17:31', state.charging_profile.prefered_charging_window.end_time)
+        self.assertEqual('05:02', state.charging_profile.preferred_charging_window.start_time)
+        self.assertEqual('17:31', state.charging_profile.preferred_charging_window.end_time)
+
+    def test_available_attributes(self):
+        """Check available_attributes for charging_profile service."""
+        account = mock.MagicMock(ConnectedDriveAccount)
+        state = VehicleState(account, None)
+        expected_attributes = ['is_pre_entry_climatization_enabled', 'pre_entry_climatization_timer',
+                               'preferred_charging_window', 'charging_preferences', 'charging_mode']
+        existing_attributes = state.charging_profile.available_attributes
+        self.assertListEqual(existing_attributes, expected_attributes)
diff --git a/test/test_last_destinations.py b/test/test_last_destinations.py
index 7db6990..d9be66d 100644
--- a/test/test_last_destinations.py
+++ b/test/test_last_destinations.py
@@ -27,3 +27,11 @@ class TestState(unittest.TestCase):
         self.assertEqual('LONDON', state.last_destinations.last_destinations[0].city)
         self.assertEqual('PITFIELD STREET', state.last_destinations.last_destinations[0].street)
         self.assertEqual('2015-09-25T08:06:11+0200', state.last_destinations.last_destinations[0].created_at)
+
+    def test_available_attributes(self):
+        """Check available_attributes for last_destination service."""
+        account = mock.MagicMock(ConnectedDriveAccount)
+        state = VehicleState(account, None)
+        expected_attributes = ['last_destinations']
+        existing_attributes = state.last_destinations.available_attributes
+        self.assertListEqual(existing_attributes, expected_attributes)
diff --git a/test/test_last_trip.py b/test/test_last_trip.py
index e35523f..0b3bf23 100644
--- a/test/test_last_trip.py
+++ b/test/test_last_trip.py
@@ -34,3 +34,14 @@ class TestState(unittest.TestCase):
         self.assertEqual(0, state.last_trip.saved_fuel)
         self.assertEqual('2015-12-01T20:44:00+0100', state.last_trip.date)
         self.assertEqual(124, state.last_trip.duration)
+
+    def test_available_attributes(self):
+        """Check available_attributes for last_trip service."""
+        account = mock.MagicMock(ConnectedDriveAccount)
+        state = VehicleState(account, None)
+        expected_attributes = ['acceleration_value', 'anticipation_value', 'auxiliary_consumption_value',
+                               'average_combined_consumption', 'average_electric_consumption', 'average_recuperation',
+                               'date', 'driving_mode_value', 'duration', 'efficiency_value', 'electric_distance',
+                               'electric_distance_ratio', 'saved_fuel', 'total_consumption_value', 'total_distance']
+        existing_attributes = state.last_trip.available_attributes
+        self.assertListEqual(existing_attributes, expected_attributes)