1818)
1919from ..connection import StickController
2020from ..exceptions import MessageError , NodeError
21- from ..messages .requests import SenseConfigureHysteresisRequest
21+ from ..messages .requests import (
22+ SenseConfigureHysteresisRequest ,
23+ SenseReportIntervalRequest ,
24+ )
2225from ..messages .responses import (
2326 NODE_SWITCH_GROUP_ID ,
2427 SENSE_REPORT_ID ,
5962CACHE_SENSE_HYSTERESIS_TEMPERATURE_UPPER_BOUND = "temperature_upper_bound"
6063CACHE_SENSE_HYSTERESIS_TEMPERATURE_LOWER_BOUND = "temperature_lower_bound"
6164CACHE_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION = "temperature_direction"
65+ CACHE_SENSE_HYSTERESIS_REPORT_INTERVAL = "report_interval"
6266CACHE_SENSE_HYSTERESIS_CONFIG_DIRTY = "sense_hysteresis_config_dirty"
6367
6468DEFAULT_SENSE_HYSTERESIS_HUMIDITY_ENABLED : Final = False
6973DEFAULT_SENSE_HYSTERESIS_TEMPERATURE_UPPER_BOUND : Final = 50.0
7074DEFAULT_SENSE_HYSTERESIS_TEMPERATURE_LOWER_BOUND : Final = 50.0
7175DEFAULT_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION : Final = True
76+ DEFAULT_SENSE_HYSTERESIS_REPORT_INTERVAL : Final = 15
7277
7378
7479class PlugwiseSense (NodeSED ):
@@ -175,6 +180,9 @@ async def _load_from_cache(self) -> bool:
175180 if (temperature_direction := self ._temperature_direction_from_cache ()) is None :
176181 dirty = True
177182 temperature_direction = DEFAULT_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION
183+ if (report_interval := self ._report_interval_from_cache ()) is None :
184+ dirty = True
185+ report_interval = DEFAULT_SENSE_HYSTERESIS_REPORT_INTERVAL
178186 dirty |= self ._sense_hysteresis_config_dirty_from_cache ()
179187
180188 self ._hysteresis_config = SenseHysteresisConfig (
@@ -186,6 +194,7 @@ async def _load_from_cache(self) -> bool:
186194 temperature_upper_bound = temperature_upper_bound ,
187195 temperature_lower_bound = temperature_lower_bound ,
188196 temperature_direction = temperature_direction ,
197+ report_interval = report_interval ,
189198 dirty = dirty ,
190199 )
191200 if dirty :
@@ -248,6 +257,14 @@ def _temperature_direction_from_cache(self) -> bool | None:
248257 """Load Temperature hysteresis switch direction from cache."""
249258 return self ._get_cache_as_bool (CACHE_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION )
250259
260+ def _report_interval_from_cache (self ) -> float | None :
261+ """Report interval from cache."""
262+ if (
263+ report_interval := self ._get_cache (CACHE_SENSE_HYSTERESIS_REPORT_INTERVAL )
264+ ) is not None :
265+ return int (report_interval )
266+ return None
267+
251268 def _sense_hysteresis_config_dirty_from_cache (self ) -> bool :
252269 """Load sense hysteresis dirty from cache."""
253270 if (
@@ -278,6 +295,7 @@ def hysteresis_config(self) -> SenseHysteresisConfig:
278295 temperature_upper_bound = self .temperature_upper_bound ,
279296 temperature_lower_bound = self .temperature_lower_bound ,
280297 temperature_direction = self .temperature_direction ,
298+ report_interval = self .report_interval ,
281299 dirty = self .hysteresis_config_dirty ,
282300 )
283301
@@ -337,6 +355,13 @@ def temperature_direction(self) -> bool:
337355 return self ._hysteresis_config .temperature_direction
338356 return DEFAULT_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION
339357
358+ @property
359+ def report_interval (self ) -> float :
360+ """Temperature lower bound value."""
361+ if self ._hysteresis_config .report_interval is not None :
362+ return self ._hysteresis_config .report_interval
363+ return DEFAULT_SENSE_HYSTERESIS_REPORT_INTERVAL
364+
340365 @property
341366 def hysteresis_config_dirty (self ) -> bool :
342367 """Sense hysteresis configuration dirty flag."""
@@ -537,6 +562,31 @@ async def set_hysteresis_temperature_lower_bound(self, lower_bound: float) -> bo
537562 await self ._sense_configure_update ()
538563 return True
539564
565+ async def set_report_interval (self , report_interval : int ) -> bool :
566+ """Configure Sense measurement interval.
567+
568+ Configuration request will be queued and will be applied the next time when node is awake for maintenance.
569+ """
570+ _LOGGER .debug (
571+ "set_report_interval | Device %s | %s -> %s" ,
572+ self .name ,
573+ self ._hysteresis_config .report_interval ,
574+ report_interval ,
575+ )
576+ if report_interval < 1 or report_interval > 60 :
577+ raise ValueError (
578+ f"Invalid measurement interval { report_interval } . It must be between 1 and 60 minutes"
579+ )
580+ if self ._hysteresis_config .report_interval == report_interval :
581+ return False
582+ self ._hysteresis_config = replace (
583+ self ._hysteresis_config ,
584+ report_interval = report_interval ,
585+ dirty = True ,
586+ )
587+ await self ._sense_configure_update ()
588+ return True
589+
540590 async def set_hysteresis_temperature_direction (self , state : bool ) -> bool :
541591 """Configure temperature hysteresis to switch on or off on increasing or decreasing direction.
542592
@@ -637,6 +687,7 @@ async def _run_awake_tasks(self) -> None:
637687 configure_result = await gather (
638688 self ._configure_sense_humidity_task (),
639689 self ._configure_sense_temperature_task (),
690+ self ._configure_sense_report_interval_task (),
640691 )
641692 if all (configure_result ):
642693 self ._hysteresis_config = replace (self ._hysteresis_config , dirty = False )
@@ -645,10 +696,11 @@ async def _run_awake_tasks(self) -> None:
645696 else :
646697 _LOGGER .warning (
647698 "Sense hysteresis configuration partially failed for %s "
648- "(humidity=%s, temperature=%s); will retry on next wake." ,
699+ "(humidity=%s, temperature=%s, report_interval=%s ); will retry on next wake." ,
649700 self .name ,
650701 configure_result [0 ],
651702 configure_result [1 ],
703+ configure_result [2 ],
652704 )
653705 await self .publish_feature_update_to_subscribers (
654706 NodeFeature .SENSE_HYSTERESIS ,
@@ -763,6 +815,35 @@ async def _configure_sense_temperature_task(self) -> bool:
763815 )
764816 return False
765817
818+ async def _configure_sense_report_interval_task (self ) -> bool :
819+ """Configure Sense report interval setting. Returns True if successful."""
820+ if not self ._hysteresis_config .dirty :
821+ return True
822+ request = SenseReportIntervalRequest (
823+ self ._send ,
824+ self ._mac_in_bytes ,
825+ self .report_interval ,
826+ )
827+ if (response := await request .send ()) is None :
828+ _LOGGER .warning (
829+ "No response from %s to configure report interval." ,
830+ self .name ,
831+ )
832+ return False
833+ if response .node_ack_type == NodeAckResponseType .SENSE_INTERVAL_FAILED :
834+ _LOGGER .warning ("Failed to configure report interval for %s" , self .name )
835+ return False
836+ if response .node_ack_type == NodeAckResponseType .SENSE_INTERVAL_ACCEPTED :
837+ _LOGGER .debug ("Successful configure report interval for %s" , self .name )
838+ return True
839+
840+ _LOGGER .warning (
841+ "Unexpected response ack type %s for %s" ,
842+ response .node_ack_type ,
843+ self .name ,
844+ )
845+ return False
846+
766847 async def _sense_configure_update (self ) -> None :
767848 """Push sense configuration update to cache."""
768849 self ._set_cache (CACHE_SENSE_HYSTERESIS_HUMIDITY_ENABLED , self .humidity_enabled )
@@ -787,6 +868,7 @@ async def _sense_configure_update(self) -> None:
787868 self ._set_cache (
788869 CACHE_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION , self .temperature_direction
789870 )
871+ self ._set_cache (CACHE_SENSE_HYSTERESIS_REPORT_INTERVAL , self .report_interval )
790872 self ._set_cache (
791873 CACHE_SENSE_HYSTERESIS_CONFIG_DIRTY , self .hysteresis_config_dirty
792874 )
0 commit comments