@@ -132,9 +132,12 @@ class Characteristic:
132132 "service" ,
133133 "_uuid_str" ,
134134 "_loader_display_name" ,
135+ "allow_invalid_client_values" ,
135136 )
136137
137- def __init__ (self , display_name , type_id , properties ):
138+ def __init__ (
139+ self , display_name , type_id , properties , allow_invalid_client_values = False
140+ ):
138141 """Initialise with the given properties.
139142
140143 :param display_name: Name that will be displayed for this
@@ -150,6 +153,15 @@ def __init__(self, display_name, type_id, properties):
150153 """
151154 _validate_properties (properties )
152155 self .broker = None
156+ #
157+ # As of iOS 15.1, Siri requests TargetHeatingCoolingState
158+ # as Auto reguardless if its a valid value or not.
159+ #
160+ # Consumers of this api may wish to set allow_invalid_client_values
161+ # to True and handle converting the Auto state to Cool or Heat
162+ # depending on the device.
163+ #
164+ self .allow_invalid_client_values = allow_invalid_client_values
153165 self .display_name = display_name
154166 self .properties = properties
155167 self .type_id = type_id
@@ -185,14 +197,22 @@ def get_value(self):
185197 self .value = self .to_valid_value (value = self .getter_callback ())
186198 return self .value
187199
200+ def valid_value_or_raise (self , value ):
201+ """Raise ValueError if PROP_VALID_VALUES is set and the value is not present."""
202+ if self .type_id in ALWAYS_NULL :
203+ return
204+ valid_values = self .properties .get (PROP_VALID_VALUES )
205+ if not valid_values :
206+ return
207+ if value in valid_values .values ():
208+ return
209+ error_msg = f"{ self .display_name } : value={ value } is an invalid value."
210+ logger .error (error_msg )
211+ raise ValueError (error_msg )
212+
188213 def to_valid_value (self , value ):
189214 """Perform validation and conversion to valid value."""
190- if self .properties .get (PROP_VALID_VALUES ):
191- if value not in self .properties [PROP_VALID_VALUES ].values ():
192- error_msg = f"{ self .display_name } : value={ value } is an invalid value."
193- logger .error (error_msg )
194- raise ValueError (error_msg )
195- elif self .properties [PROP_FORMAT ] == HAP_FORMAT_STRING :
215+ if self .properties [PROP_FORMAT ] == HAP_FORMAT_STRING :
196216 value = str (value )[
197217 : self .properties .get (HAP_REPR_MAX_LEN , DEFAULT_MAX_LENGTH )
198218 ]
@@ -241,6 +261,7 @@ def override_properties(self, properties=None, valid_values=None):
241261
242262 try :
243263 self .value = self .to_valid_value (self .value )
264+ self .valid_value_or_raise (self .value )
244265 except ValueError :
245266 self .value = self ._get_default_value ()
246267
@@ -265,6 +286,7 @@ def set_value(self, value, should_notify=True):
265286 """
266287 logger .debug ("set_value: %s to %s" , self .display_name , value )
267288 value = self .to_valid_value (value )
289+ self .valid_value_or_raise (value )
268290 changed = self .value != value
269291 self .value = value
270292 if changed and should_notify and self .broker :
@@ -280,20 +302,23 @@ def client_update_value(self, value, sender_client_addr=None):
280302 original_value = value
281303 if self .type_id not in ALWAYS_NULL or original_value is not None :
282304 value = self .to_valid_value (value )
305+ if not self .allow_invalid_client_values :
306+ self .valid_value_or_raise (value )
283307 logger .debug (
284308 "client_update_value: %s to %s (original: %s) from client: %s" ,
285309 self .display_name ,
286310 value ,
287311 original_value ,
288312 sender_client_addr ,
289313 )
290- changed = self .value != value
314+ previous_value = self .value
291315 self .value = value
292- if changed :
293- self .notify (sender_client_addr )
294316 if self .setter_callback :
295317 # pylint: disable=not-callable
296318 self .setter_callback (value )
319+ changed = self .value != previous_value
320+ if changed :
321+ self .notify (sender_client_addr )
297322 if self .type_id in ALWAYS_NULL :
298323 self .value = None
299324
0 commit comments