HAP-python 1.0.6__tar.gz → 1.0.7__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.

Potentially problematic release.


This version of HAP-python might be problematic. Click here for more details.

Files changed (35) hide show
  1. {HAP-python-1.0.6 → HAP-python-1.0.7}/HAP_python.egg-info/PKG-INFO +1 -1
  2. {HAP-python-1.0.6 → HAP-python-1.0.7}/HAP_python.egg-info/SOURCES.txt +1 -0
  3. {HAP-python-1.0.6 → HAP-python-1.0.7}/HAP_python.egg-info/requires.txt +4 -0
  4. {HAP-python-1.0.6 → HAP-python-1.0.7}/PKG-INFO +1 -1
  5. HAP-python-1.0.7/pyhap/accessories/SDS011.py +152 -0
  6. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/characteristic.py +72 -43
  7. {HAP-python-1.0.6 → HAP-python-1.0.7}/setup.py +7 -1
  8. {HAP-python-1.0.6 → HAP-python-1.0.7}/HAP_python.egg-info/dependency_links.txt +0 -0
  9. {HAP-python-1.0.6 → HAP-python-1.0.7}/HAP_python.egg-info/top_level.txt +0 -0
  10. {HAP-python-1.0.6 → HAP-python-1.0.7}/LICENSE +0 -0
  11. {HAP-python-1.0.6 → HAP-python-1.0.7}/MANIFEST.in +0 -0
  12. {HAP-python-1.0.6 → HAP-python-1.0.7}/README.md +0 -0
  13. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/__init__.py +0 -0
  14. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/AM2302.py +0 -0
  15. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/BMP180.py +0 -0
  16. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/FakeFan.py +0 -0
  17. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/Http.py +0 -0
  18. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/LightBulb.py +0 -0
  19. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/MotionSensor.py +0 -0
  20. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/ShutdownSwitch.py +0 -0
  21. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/TSL2591.py +0 -0
  22. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/TemperatureSensor.py +0 -0
  23. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessories/__init__.py +0 -0
  24. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessory.py +0 -0
  25. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/accessory_driver.py +0 -0
  26. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/hap_server.py +0 -0
  27. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/hsrp.py +0 -0
  28. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/loader.py +0 -0
  29. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/params.py +0 -0
  30. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/resources/characteristics.json +0 -0
  31. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/resources/services.json +0 -0
  32. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/service.py +0 -0
  33. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/tlv.py +0 -0
  34. {HAP-python-1.0.6 → HAP-python-1.0.7}/pyhap/util.py +0 -0
  35. {HAP-python-1.0.6 → HAP-python-1.0.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: HAP-python
3
- Version: 1.0.6
3
+ Version: 1.0.7
4
4
  Summary: HomeKit Accessory Protocol implementation in python3
5
5
  Home-page: https://github.com/ikalchev/HAP-python.git
6
6
  Author: Ivan Kalchev
@@ -25,6 +25,7 @@ pyhap/accessories/FakeFan.py
25
25
  pyhap/accessories/Http.py
26
26
  pyhap/accessories/LightBulb.py
27
27
  pyhap/accessories/MotionSensor.py
28
+ pyhap/accessories/SDS011.py
28
29
  pyhap/accessories/ShutdownSwitch.py
29
30
  pyhap/accessories/TSL2591.py
30
31
  pyhap/accessories/TemperatureSensor.py
@@ -3,3 +3,7 @@ tlslite-ng
3
3
  ed25519
4
4
  zeroconf
5
5
  curve25519-donna
6
+
7
+ [dev]
8
+ pytest
9
+ tox
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: HAP-python
3
- Version: 1.0.6
3
+ Version: 1.0.7
4
4
  Summary: HomeKit Accessory Protocol implementation in python3
5
5
  Home-page: https://github.com/ikalchev/HAP-python.git
6
6
  Author: Ivan Kalchev
@@ -0,0 +1,152 @@
1
+ """
2
+ An Accessory wrapper for the SDS011 air particulate density sensor.
3
+
4
+ The density sensor implementation can be found here:
5
+ https://github.com/ikalchev/py-sds011
6
+ Place the file under a package named sensors in your python path,
7
+ or change the import alltogether.
8
+ """
9
+ import time
10
+ import logging
11
+
12
+ from sensors.SDS011 import SDS011 as AirSensor
13
+ from pyhap.accessory import Accessory, Category
14
+ import pyhap.loader as loader
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class SDS011(Accessory):
20
+ """Accessory wrapper for SDS011.
21
+ """
22
+
23
+ category = Category.SENSOR
24
+
25
+ SORTED_PM_QUALITY_MAP = ((200, 5), (150, 4), (100, 3), (50, 2), (0, 1))
26
+ """
27
+ Threshold-to-state tuples. These show the state for which the threshold is
28
+ lower boundry. Uses something like Air Quality Index (AQI).
29
+
30
+ The UI shows:
31
+ 1 - Excellent
32
+ 2 - Good
33
+ 3 - Fair
34
+ 4 - Inferior
35
+ 5 - Poor
36
+ """
37
+
38
+ def __init__(self, serial_port, *args, sleep_duration_s=15*60, calib_duration_s=15,
39
+ **kwargs):
40
+ """Initialise and start SDS011 on the given port.
41
+
42
+ @param serial_port: The SDS011 port, e.g. /dev/ttyUSB0.
43
+ @type serial_port: str
44
+
45
+ @param sleep_duration_s: The amount of time in seconds the sensor sleeps before
46
+ waking it up for reading. Defaults to 15 minutes.
47
+ @type sleep_duration_s: float
48
+
49
+ @param calib_duration_s: The amount of time in seconds to wait after the sensor
50
+ has been waken up before reading its data. Defaults to 15 seconds.
51
+ @type calib_duration_s: float
52
+ """
53
+ self.pm25_quality = None
54
+ self.pm25_density = None
55
+ self.pm10_quality = None
56
+ self.pm10_density = None
57
+ super(SDS011, self).__init__(*args, **kwargs)
58
+ self.sleep_duration_s = sleep_duration_s
59
+ self.calib_duration_s = calib_duration_s
60
+ self.serial_port = serial_port
61
+ self.sensor = AirSensor(serial_port)
62
+ self.sensor.sleep(sleep=False)
63
+
64
+ def _set_services(self):
65
+ """Add the AirQualitySensor services (one for PM2.5 and one for PM10).
66
+
67
+ Also adds and configures optional characteristics, such as Name,
68
+ AirParticulateSize, AirParticulateDensity.
69
+ """
70
+ super(SDS011, self)._set_services()
71
+ char_loader = loader.get_char_loader()
72
+
73
+ # PM2.5
74
+ air_quality_pm25 = loader.get_serv_loader().get("AirQualitySensor")
75
+ pm25_size = char_loader.get("AirParticulateSize")
76
+ pm25_size.set_value(0, should_notify=False)
77
+ self.pm25_density = char_loader.get("AirParticulateDensity")
78
+ pm25_name = char_loader.get("Name")
79
+ pm25_name.set_value("PM2.5", should_notify=False)
80
+ self.pm25_quality = air_quality_pm25.get_characteristic("AirQuality")
81
+ air_quality_pm25.add_opt_characteristic(pm25_name, pm25_size, self.pm25_density)
82
+
83
+ # PM10
84
+ air_quality_pm10 = loader.get_serv_loader().get("AirQualitySensor")
85
+ pm10_size = char_loader.get("AirParticulateSize")
86
+ pm10_size.set_value(1, should_notify=False)
87
+ self.pm10_density = char_loader.get("AirParticulateDensity")
88
+ pm10_name = char_loader.get("Name")
89
+ pm10_name.set_value("PM10", should_notify=False)
90
+ self.pm10_quality = air_quality_pm10.get_characteristic("AirQuality")
91
+ air_quality_pm10.add_opt_characteristic(pm10_name, pm10_size, self.pm10_density)
92
+
93
+ self.add_service(air_quality_pm25)
94
+ self.add_service(air_quality_pm10)
95
+
96
+ def __getstate__(self):
97
+ """Get the state, less the sensor.
98
+ """
99
+ state = super(SDS011, self).__getstate__()
100
+ state["sensor"] = None
101
+ return state
102
+
103
+ def __setstate__(self, state):
104
+ """Set the state of this Accessory and initialise the sensor
105
+ with the serial port in the state.
106
+ """
107
+ self.__dict__.update(state)
108
+ self.sensor = AirSensor(self.serial_port)
109
+
110
+ def get_quality_classification(self, pm, is_pm25=False):
111
+ """Get the air quality clasification based on the PM density.
112
+
113
+ Uses Air Quality Index (AQI), without averaging for an hour.
114
+
115
+ @see: SDS011.SORTED_PM_QUALITY_MAP
116
+
117
+ @rtype: int
118
+ """
119
+ assert pm >= 0
120
+ return next(state for threshold, state in self.SORTED_PM_QUALITY_MAP
121
+ if threshold <= pm)
122
+
123
+ def run(self):
124
+ """Start updating the air quality readings.
125
+
126
+ Initially, we read from the sensor and update the values. Then we put
127
+ it in sleep mode and while the sentinel is not set:
128
+ - Sleep for `self.sleep_duration_s`.
129
+ - Wake up and wait `self.calib_duration_s` seconds.
130
+ - Get the sensor's readings and update.
131
+ """
132
+ pm25, pm10 = self.sensor.query()
133
+ self.pm25_density.set_value(pm25)
134
+ self.pm25_quality.set_value(
135
+ self.get_quality_classification(pm25, is_pm25=True))
136
+ self.pm10_density.set_value(pm10)
137
+ self.pm10_quality.set_value(
138
+ self.get_quality_classification(pm10, is_pm25=False))
139
+ self.sensor.sleep()
140
+ while not self.run_sentinel.wait(self.sleep_duration_s):
141
+ logger.debug("Waking up sensor.")
142
+ self.sensor.sleep(sleep=False)
143
+ time.sleep(self.calib_duration_s)
144
+ pm25, pm10 = self.sensor.query()
145
+ self.pm25_density.set_value(pm25)
146
+ self.pm25_quality.set_value(
147
+ self.get_quality_classification(pm25, is_pm25=True))
148
+ self.pm10_density.set_value(pm10)
149
+ self.pm10_quality.set_value(
150
+ self.get_quality_classification(pm10, is_pm25=False))
151
+ self.sensor.sleep()
152
+ logger.debug("Read cycle done. Sleeping.")
@@ -1,5 +1,9 @@
1
- # All things for a HAP characteristic.
1
+ """
2
+ All things for a HAP characteristic.
2
3
 
4
+ A Characteristic is the smallest unit of the smart home, e.g.
5
+ a temperature measuring or a device status.
6
+ """
3
7
 
4
8
  class HAP_FORMAT:
5
9
  BOOL = 'bool'
@@ -52,39 +56,59 @@ class CharacteristicError(Exception):
52
56
  pass
53
57
 
54
58
 
59
+ _HAP_NUMERIC_FIELDS = {"maxValue", "minValue", "minStep", "unit"}
55
60
  """Fields that should be included in the HAP representation of the characteristic.
56
61
 
57
62
  That is, if they are present in the specification of a numeric-value characteristic.
58
63
  """
59
- _HAP_NUMERIC_FIELDS = {"maxValue", "minValue", "minStep", "unit"}
60
64
 
61
65
 
62
66
  class Characteristic(object):
67
+ """Represents a HAP characteristic, the smallest unit of the smart home.
68
+
69
+ A HAP characteristic is some measurement or state, like battery status or
70
+ the current temperature. Characteristics are contained in services.
71
+ Each charactaristic has a unique type UUID and a set of properties,
72
+ like format, min and max values, valid values and others.
73
+ """
63
74
 
64
75
  def __init__(self, display_name, type_id, properties, value=None, broker=None):
76
+ assert "Format" in properties and "Permissions" in properties
65
77
  self.display_name = display_name
66
78
  self.type_id = type_id
67
- assert "Format" in properties and "Permissions" in properties
68
79
  self.properties = properties
69
- self.allowed_values = self.properties.get("ValidValues")
80
+ self.has_valid_values = "ValidValues" in self.properties
70
81
  self.value = value or HAP_FORMAT.DEFAULT[properties["Format"]]
71
82
  self.broker = broker
72
83
  self.setter_callback = None
73
- self.hap_value_template = self._create_value_HAP_template()
84
+ self.hap_template = self._create_hap_template()
85
+
86
+ def _create_hap_template(self):
87
+ """Create a HAP template for describing this Characteristic.
88
+
89
+ Contains properties that do not change or change rarely, e.g. the type.
90
+ """
91
+ template = dict()
92
+ if self.properties["Format"] in HAP_FORMAT.NUMERIC:
93
+ template = {k: self.properties[k]
94
+ for k in self.properties.keys() & _HAP_NUMERIC_FIELDS}
95
+ return template
74
96
 
75
97
  def set_value(self, value, should_notify=True):
76
- """Set the given value.
98
+ """Set the given raw value. It is checked if it is a valid value.
77
99
 
78
100
  @param value: The value to assign as this Characteristic's value.
79
101
  @type value: Depends on properties["Format"]
80
102
 
81
103
  @param should_notify: Whether a the change should be sent to subscribed clients.
104
+ The notification is called _after_ the setter callback.
82
105
  @type should_notify: bool
83
106
 
84
- @raise ValueError: When the value being assigned is not one of the allowed values
107
+ @raise ValueError: When the value being assigned is not one of the valid values
85
108
  for this Characteristic.
86
109
  """
87
- if self.allowed_values is not None and value not in self.allowed_values.values():
110
+ if (self.has_valid_values
111
+ and value not in self.properties["ValidValues"].values()):
88
112
  raise ValueError
89
113
  self.value = value
90
114
  if self.setter_callback is not None:
@@ -93,53 +117,49 @@ class Characteristic(object):
93
117
  self.notify()
94
118
 
95
119
  def get_value(self):
96
- return self.value
97
-
98
- def notify(self):
99
- data = {
100
- "type_id": self.type_id,
101
- "value": self.value,
102
- }
103
- self.broker.publish(data, self)
120
+ """Get the raw value of this Characteristic.
104
121
 
105
- def _create_value_HAP_template(self):
106
- template = dict()
107
- if self.properties["Format"] in HAP_FORMAT.NUMERIC:
108
- template = {k: self.properties[k]
109
- for k in self.properties.keys() & _HAP_NUMERIC_FIELDS}
110
- return template
111
-
112
- def _value_to_HAP(self):
113
- hap_rep = self.hap_value_template.copy()
122
+ @deprecated: Use self.value instead.
123
+ """
124
+ return self.value
114
125
 
126
+ def get_hap_value(self):
127
+ """Get the value of the characteristic, constrained with the HAP properties.
128
+ """
129
+ val = self.value
115
130
  if self.properties["Format"] == HAP_FORMAT.STRING:
116
- val = self.value[:256]
117
- if len(self.value) > 64:
118
- hap_rep["maxLen"] = min(len(self.value), 256)
131
+ val = val[:256]
119
132
  elif self.properties["Format"] in HAP_FORMAT.NUMERIC:
120
- val = self.value
121
- if "maxValue" in hap_rep:
122
- val = min(self.properties["maxValue"], self.value)
123
- if "minValue" in hap_rep:
124
- val = max(self.properties["minValue"], self.value)
125
- else:
126
- val = self.value
133
+ if "maxValue" in self.properties:
134
+ val = min(self.properties["maxValue"], val)
135
+ if "minValue" in self.properties:
136
+ val = max(self.properties["minValue"], val)
137
+ return val
127
138
 
128
- if HAP_PERMISSIONS.READ in self.properties["Permissions"]:
129
- hap_rep["value"] = val
139
+ def notify(self):
140
+ """Notify clients about a value change.
130
141
 
131
- return hap_rep
142
+ @note: Non-blocking, i.e. does not wait for the update to be sent.
143
+ @note: Uses the `get_hap_value`, i.e. sends the HAP value.
144
+ @see: accessory_driver.publish
145
+ """
146
+ data = {
147
+ "type_id": self.type_id,
148
+ "value": self.get_hap_value(),
149
+ }
150
+ self.broker.publish(data, self)
132
151
 
133
- def to_HAP(self, iid_manager=None):
152
+ def to_HAP(self, iid_manager):
134
153
  """Create a HAP representation of this Characteristic.
135
154
 
136
- @param base_iid: The IID for this characteristic.
137
- @type base_iid: int
155
+ @note: Uses the `get_hap_value`, i.e. sends the HAP value.
156
+
157
+ @param iid_manager: IID manager to query for this object's IID.
158
+ @type iid_manager: IIDManager
138
159
 
139
160
  @return: A HAP representation.
140
161
  @rtype: dict
141
162
  """
142
- assert iid_manager is not None
143
163
  hap_rep = {
144
164
  "iid": iid_manager.get_iid(self),
145
165
  "type": str(self.type_id).upper(),
@@ -147,5 +167,14 @@ class Characteristic(object):
147
167
  "perms": self.properties["Permissions"],
148
168
  "format": self.properties["Format"],
149
169
  }
150
- hap_rep.update(self._value_to_HAP())
170
+
171
+ value_info = self.hap_template.copy()
172
+ val = self.get_hap_value()
173
+ if self.properties["Format"] == HAP_FORMAT.STRING:
174
+ if len(val) > 64:
175
+ value_info["maxLen"] = min(len(val), 256)
176
+ if HAP_PERMISSIONS.READ in self.properties["Permissions"]:
177
+ value_info["value"] = val
178
+
179
+ hap_rep.update(value_info)
151
180
  return hap_rep
@@ -4,7 +4,7 @@ setup(
4
4
  name="HAP-python",
5
5
  description="HomeKit Accessory Protocol implementation in python3",
6
6
  author="Ivan Kalchev",
7
- version="1.0.6",
7
+ version="1.0.7",
8
8
  url="https://github.com/ikalchev/HAP-python.git",
9
9
  classifiers=[
10
10
  "Development Status :: 5 - Production/Stable",
@@ -27,6 +27,12 @@ setup(
27
27
  "zeroconf",
28
28
  "curve25519-donna"
29
29
  ],
30
+ extras_require={
31
+ "dev": [
32
+ "pytest",
33
+ "tox",
34
+ ],
35
+ },
30
36
  package_data={
31
37
  "pyhap": ["resources/*"],
32
38
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes