async def _async_update_interval(now): try: await async_update(now) finally: if not opp.is_stopping: async_track_point_in_utc_time(opp, _async_update_interval, util.dt.utcnow() + interval)
async def async_update_user_data(now): """Update user data from eight in USER_SCAN_INTERVAL.""" await eight.update_user_data() async_dispatcher_send(opp, SIGNAL_UPDATE_USER) async_track_point_in_utc_time(opp, async_update_user_data, utcnow() + USER_SCAN_INTERVAL)
async def async_update_heat_data(now): """Update heat data from eight in HEAT_SCAN_INTERVAL.""" await eight.update_device_data() async_dispatcher_send(opp, SIGNAL_UPDATE_HEAT) async_track_point_in_utc_time(opp, async_update_heat_data, utcnow() + HEAT_SCAN_INTERVAL)
def update_sun_position(self, now=None): """Calculate the position of the sun.""" # Grab current time in case system clock changed since last time we ran. utc_point_in_time = dt_util.utcnow() self.solar_azimuth = round( self.location.solar_azimuth(utc_point_in_time, self.elevation), 2) self.solar_elevation = round( self.location.solar_elevation(utc_point_in_time, self.elevation), 2) _LOGGER.debug( "sun position_update@%s: elevation=%s azimuth=%s", utc_point_in_time.isoformat(), self.solar_elevation, self.solar_azimuth, ) self.async_write_op_state() # Next update as per the current phase delta = _PHASE_UPDATES[self.phase] # if the next update is within 1.25 of the next # position update just drop it if utc_point_in_time + delta * 1.25 > self._next_change: return event.async_track_point_in_utc_time(self.opp, self.update_sun_position, utc_point_in_time + delta)
async def async_handle_start_charge(service): """Handle service to start charging.""" # It would be better if this was changed to use nickname, or # an entity name rather than a vin. vin = service.data[ATTR_VIN] if vin in opp.data[DATA_LEAF]: data_store = opp.data[DATA_LEAF][vin] # Send the command to request charging is started to Nissan # servers. If that completes OK then trigger a fresh update to # pull the charging status from the car after waiting a minute # for the charging request to reach the car. result = await opp.async_add_executor_job( data_store.leaf.start_charging) if result: _LOGGER.debug( "Start charging sent, request updated data in 1 minute") check_charge_at = utcnow() + timedelta(minutes=1) data_store.next_update = check_charge_at async_track_point_in_utc_time(opp, data_store.async_update_data, check_charge_at) else: _LOGGER.debug("Vin %s not recognised for update", vin)
async def scan_devices(now): """Scan for devices.""" try: results = await opp.async_add_job(_discover, netdisco) for result in results: opp.async_create_task(new_service_found(*result)) except OSError: logger.error("Network is unreachable") async_track_point_in_utc_time( opp, scan_devices, dt_util.utcnow() + SCAN_INTERVAL )
def message_received(msg): """Handle new MQTT messages.""" payload = msg.payload # auto-expire enabled? expire_after = self._config.get(CONF_EXPIRE_AFTER) if expire_after is not None and expire_after > 0: # When expire_after is set, and we receive a message, assume device is not expired since it has to be to receive the message self._expired = False # Reset old trigger if self._expiration_trigger: self._expiration_trigger() self._expiration_trigger = None # Set new trigger expiration_at = dt_util.utcnow() + timedelta( seconds=expire_after) self._expiration_trigger = async_track_point_in_utc_time( self.opp, self._value_is_expired, expiration_at) template = self._config.get(CONF_VALUE_TEMPLATE) if template is not None: variables = {"entity_id": self.entity_id} payload = template.async_render_with_possible_json_value( payload, self._state, variables=variables, ) self._state = payload self.async_write_op_state()
async def _async_delay(self, action, variables, context): """Handle delay.""" # Call ourselves in the future to continue work unsub = None @callback def async_script_delay(now): """Handle delay.""" with suppress(ValueError): self._async_listener.remove(unsub) self.opp.async_create_task(self.async_run(variables, context)) delay = action[CONF_DELAY] try: if isinstance(delay, template.Template): delay = vol.All(cv.time_period, cv.positive_timedelta)( delay.async_render(variables)) elif isinstance(delay, dict): delay_data = {} delay_data.update(template.render_complex(delay, variables)) delay = cv.time_period(delay_data) except (exceptions.TemplateError, vol.Invalid) as ex: _LOGGER.error("Error rendering '%s' delay template: %s", self.name, ex) raise _StopScript self.last_action = action.get(CONF_ALIAS, f"delay {delay}") self._log("Executing step %s" % self.last_action) unsub = async_track_point_in_utc_time(self.opp, async_script_delay, date_util.utcnow() + delay) self._async_listener.append(unsub) raise _SuspendScript
async def update(now): """Update status from the online service.""" try: if not await connection.update(journal=True): _LOGGER.warning("Could not query server") return False for vehicle in connection.vehicles: if vehicle.vin not in data.vehicles: discover_vehicle(vehicle) async_dispatcher_send(opp, SIGNAL_STATE_UPDATED) return True finally: async_track_point_in_utc_time(opp, update, utcnow() + interval)
def point_in_time_listener(self, time_date): """Get the latest data and update state.""" self._update_internal_state(time_date) self.async_write_op_state() self.unsub = async_track_point_in_utc_time( self.opp, self.point_in_time_listener, self.get_next_interval() )
def _restart_timer(self): """Restart the 25 hour timer.""" try: self._heartbeat_timer() self._heartbeat_timer = None except TypeError: # No heartbeat timer is active pass @callback def timer_elapsed(now) -> None: """Heartbeat missed; set state to ON to indicate dead battery.""" self._computed_state = True self._heartbeat_timer = None self.async_write_op_state() point_in_time = dt_util.utcnow() + timedelta(hours=25) _LOGGER.debug( "Heartbeat timer starting. Now: %s Then: %s", dt_util.utcnow(), point_in_time, ) self._heartbeat_timer = async_track_point_in_utc_time( self.opp, timer_elapsed, point_in_time)
def _point_in_time_listener(self, now): """Run when the state of the sensor should be updated.""" self._calculate_next_update() self.async_write_op_state() self._unsub_update = event.async_track_point_in_utc_time( self.opp, self._point_in_time_listener, self._next_update)
def schedule_light_turn_on(now): """Turn on all the lights at the moment sun sets. We will schedule to have each light start after one another and slowly transition in. """ start_point = calc_time_for_light_when_sunset() if not start_point: return for index, light_id in enumerate(light_ids): async_track_point_in_utc_time( opp, async_turn_on_factory(light_id), start_point + index * LIGHT_TRANSITION_TIME, )
async def update_image(self, image, filename): """Update the camera image.""" if self._state == STATE_IDLE: self._state = STATE_RECORDING self._last_trip = dt_util.utcnow() self.queue.clear() self._filename = filename self.queue.appendleft(image) @callback def reset_state(now): """Set state to idle after no new images for a period of time.""" self._state = STATE_IDLE self._expired_listener = None _LOGGER.debug("Reset state") self.async_write_op_state() if self._expired_listener: self._expired_listener() self._expired_listener = async_track_point_in_utc_time( self.opp, reset_state, dt_util.utcnow() + self._timeout ) self.async_write_op_state()
async def async_start(self, duration): """Start a timer.""" if self._listener: self._listener() self._listener = None newduration = None if duration: newduration = duration event = EVENT_TIMER_STARTED if self._state == STATUS_ACTIVE or self._state == STATUS_PAUSED: event = EVENT_TIMER_RESTARTED self._state = STATUS_ACTIVE start = dt_util.utcnow().replace(microsecond=0) if self._remaining and newduration is None: self._end = start + self._remaining else: if newduration: self._config[CONF_DURATION] = newduration self._remaining = newduration else: self._remaining = self._config[CONF_DURATION] self._end = start + self._config[CONF_DURATION] self.opp.bus.async_fire(event, {"entity_id": self.entity_id}) self._listener = async_track_point_in_utc_time(self.opp, self.async_finished, self._end) self.async_write_op_state()
async def async_setup_platform(opp, config, async_add_entities, discovery_info=None): """Set up the Time and Date sensor.""" if opp.config.time_zone is None: _LOGGER.error("Timezone is not set in Open Peer Power configuration") return False devices = [] for variable in config[CONF_DISPLAY_OPTIONS]: device = TimeDateSensor(opp, variable) async_track_point_in_utc_time(opp, device.point_in_time_listener, device.get_next_interval()) devices.append(device) async_add_entities(devices, True)
def _schedule_refresh(self) -> None: """Schedule a refresh.""" if self._unsub_refresh: self._unsub_refresh() self._unsub_refresh = None self._unsub_refresh = async_track_point_in_utc_time( self.opp, self._handle_refresh_interval, utcnow() + self.update_interval )
def _async_track_unavailable(self): if self._remove_unavailability_tracker: self._remove_unavailability_tracker() self._remove_unavailability_tracker = async_track_point_in_utc_time( self.opp, self._async_set_unavailable, utcnow() + TIME_TILL_UNAVAILABLE) if not self._is_available: self._is_available = True return True return False
async def async_turn_on(self, **kwargs): """Power the relay.""" if self._reset_sub is not None: self._reset_sub() self._reset_sub = None self._reset_sub = async_track_point_in_utc_time( self.opp, self._async_turn_off, dt_util.utcnow() + self._time ) await self.opp.async_add_executor_job(self._turn_on) self.async_write_op_state()
def setup_leaf(car_config): """Set up a car.""" _LOGGER.debug("Logging into You+Nissan") username = car_config[CONF_USERNAME] password = car_config[CONF_PASSWORD] region = car_config[CONF_REGION] leaf = None try: # This might need to be made async (somehow) causes # openpeerpower to be slow to start sess = Session(username, password, region) leaf = sess.get_leaf() except KeyError: _LOGGER.error( "Unable to fetch car details..." " do you actually have a Leaf connected to your account?") return False except CarwingsError: _LOGGER.error( "An unknown error occurred while connecting to Nissan: %s", sys.exc_info()[0], ) return False _LOGGER.warning( "WARNING: This may poll your Leaf too often, and drain the 12V" " battery. If you drain your cars 12V battery it WILL NOT START" " as the drive train battery won't connect." " Don't set the intervals too low") data_store = LeafDataStore(opp, leaf, car_config) opp.data[DATA_LEAF][leaf.vin] = data_store for platform in PLATFORMS: load_platform(opp, platform, DOMAIN, {}, car_config) async_track_point_in_utc_time(opp, data_store.async_update_data, utcnow() + INITIAL_UPDATE)
def state_message_received(msg): """Handle a new received MQTT state message.""" payload = msg.payload # auto-expire enabled? expire_after = self._config.get(CONF_EXPIRE_AFTER) if expire_after is not None and expire_after > 0: # When expire_after is set, and we receive a message, assume device is not expired since it has to be to receive the message self._expired = False # Reset old trigger if self._expiration_trigger: self._expiration_trigger() self._expiration_trigger = None # Set new trigger expiration_at = dt_util.utcnow() + timedelta( seconds=expire_after) self._expiration_trigger = async_track_point_in_utc_time( self.opp, self.value_is_expired, expiration_at) value_template = self._config.get(CONF_VALUE_TEMPLATE) if value_template is not None: payload = value_template.async_render_with_possible_json_value( payload, variables={"entity_id": self.entity_id}) if payload == self._config[CONF_PAYLOAD_ON]: self._state = True elif payload == self._config[CONF_PAYLOAD_OFF]: self._state = False else: # Payload is not for this entity _LOGGER.warning( "No matching payload found for entity: %s with state topic: %s. Payload: %s, with value template %s", self._config[CONF_NAME], self._config[CONF_STATE_TOPIC], payload, value_template, ) return if self._delay_listener is not None: self._delay_listener() self._delay_listener = None off_delay = self._config.get(CONF_OFF_DELAY) if self._state and off_delay is not None: self._delay_listener = evt.async_call_later( self.opp, off_delay, off_delay_listener) self.async_write_op_state()
def update_sun_position(self, utc_point_in_time): """Calculate the position of the sun.""" self.solar_azimuth = round( self.location.solar_azimuth(utc_point_in_time), 2) self.solar_elevation = round( self.location.solar_elevation(utc_point_in_time), 2) _LOGGER.debug( "sun position_update@%s: elevation=%s azimuth=%s", utc_point_in_time.isoformat(), self.solar_elevation, self.solar_azimuth, ) self.async_write_op_state() # Next update as per the current phase delta = _PHASE_UPDATES[self.phase] # if the next update is within 1.25 of the next # position update just drop it if utc_point_in_time + delta * 1.25 > self._next_change: return async_track_point_in_utc_time(self.opp, self.update_sun_position, utc_point_in_time + delta)
async def async_added_to_opp(self): """Call when entity about to be added to Open Peer Power.""" self._calculate_boudary_time() self._calculate_next_update() @callback def _clean_up_listener(): if self._unsub_update is not None: self._unsub_update() self._unsub_update = None self.async_on_remove(_clean_up_listener) self._unsub_update = event.async_track_point_in_utc_time( self.opp, self._point_in_time_listener, self._next_update)
async def update_during_transition(self, when): """Update state at the start and end of a transition.""" if self.postponed_update: self.postponed_update() # Transition has started await self.update_opp() # Transition has ended if when > 0: self.postponed_update = async_track_point_in_utc_time( self.opp, self.update_opp, util.dt.utcnow() + timedelta(milliseconds=when), )
def _async_handle_update(self, *args, **kwargs) -> None: """Update the state using webhook data.""" if self._cancel_update: self._cancel_update() self._cancel_update = None if args[0][0][KEY_SUBTYPE] == SUBTYPE_RAIN_DELAY_ON: endtime = parse_datetime(args[0][0][KEY_RAIN_DELAY_END]) _LOGGER.debug("Rain delay expires at %s", endtime) self._state = True self._cancel_update = async_track_point_in_utc_time( self.opp, self._delay_expiration, endtime) elif args[0][0][KEY_SUBTYPE] == SUBTYPE_RAIN_DELAY_OFF: self._state = False self.async_write_op_state()
async def async_update_data(self, now): """Update data from nissan leaf.""" # Prevent against a previously scheduled update and an ad-hoc update # started from an update from both being triggered. if self._remove_listener: self._remove_listener() self._remove_listener = None # Clear next update whilst this update is underway self.next_update = None await self.async_refresh_data(now) self.next_update = self.get_next_interval() _LOGGER.debug("Next update=%s", self.next_update) self._remove_listener = async_track_point_in_utc_time( self.opp, self.async_update_data, self.next_update)
def _schedule_refresh(self) -> None: """Schedule a refresh.""" if self._unsub_refresh: self._unsub_refresh() self._unsub_refresh = None # We _floor_ utcnow to create a schedule on a rounded second, # minimizing the time between the point and the real activation. # That way we obtain a constant update frequency, # as long as the update process takes less than a second if self.last_update_success: update_interval = self.update_interval self.last_update_success_time = utcnow() else: update_interval = self.failed_update_interval self._unsub_refresh = async_track_point_in_utc_time( self.opp, self._handle_refresh_interval, utcnow().replace(microsecond=0) + update_interval, )
async def async_added_to_opp(self): """Subscribe to updates.""" if KEY_RAIN_DELAY in self._controller.init_data: self._state = self._controller.init_data[ KEY_RAIN_DELAY] / 1000 > as_timestamp(now()) # If the controller was in a rain delay state during a reboot, this re-sets the timer if self._state is True: delay_end = utc_from_timestamp( self._controller.init_data[KEY_RAIN_DELAY] / 1000) _LOGGER.debug("Re-setting rain delay timer for %s", delay_end) self._cancel_update = async_track_point_in_utc_time( self.opp, self._delay_expiration, delay_end) self.async_on_remove( async_dispatcher_connect( self.opp, SIGNAL_RACHIO_RAIN_DELAY_UPDATE, self._async_handle_any_update, ))
def message_received(msg): """Handle new MQTT messages.""" payload = msg.payload # auto-expire enabled? expire_after = self._config.get(CONF_EXPIRE_AFTER) if expire_after is not None and expire_after > 0: # Reset old trigger if self._expiration_trigger: self._expiration_trigger() self._expiration_trigger = None # Set new trigger expiration_at = dt_util.utcnow() + timedelta(seconds=expire_after) self._expiration_trigger = async_track_point_in_utc_time( self.opp, self.value_is_expired, expiration_at ) json_attributes = set(self._config[CONF_JSON_ATTRS]) if json_attributes: self._attributes = {} try: json_dict = json.loads(payload) if isinstance(json_dict, dict): attrs = { k: json_dict[k] for k in json_attributes & json_dict.keys() } self._attributes = attrs else: _LOGGER.warning("JSON result was not a dictionary") except ValueError: _LOGGER.warning("MQTT payload could not be parsed as JSON") _LOGGER.debug("Erroneous JSON: %s", payload) if template is not None: payload = template.async_render_with_possible_json_value( payload, self._state ) self._state = payload self.async_write_op_state()
def _async_set_timeout(self, action, variables, context, continue_on_timeout): """Schedule a timeout to abort or continue script.""" timeout = action[CONF_TIMEOUT] unsub = None @callback def async_script_timeout(now): """Call after timeout is retrieve.""" with suppress(ValueError): self._async_listener.remove(unsub) # Check if we want to continue to execute # the script after the timeout if continue_on_timeout: self.opp.async_create_task(self.async_run(variables, context)) else: self._log("Timeout reached, abort script.") self.async_stop() unsub = async_track_point_in_utc_time(self.opp, async_script_timeout, date_util.utcnow() + timeout) self._async_listener.append(unsub)