async def ws_get_statistics_during_period( opp: OpenPeerPower, connection: websocket_api.ActiveConnection, msg: dict ) -> None: """Handle statistics websocket command.""" start_time_str = msg["start_time"] end_time_str = msg.get("end_time") start_time = dt_util.parse_datetime(start_time_str) if start_time: start_time = dt_util.as_utc(start_time) else: connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") return if end_time_str: end_time = dt_util.parse_datetime(end_time_str) if end_time: end_time = dt_util.as_utc(end_time) else: connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") return else: end_time = None statistics = await opp.async_add_executor_job( statistics_during_period, opp, start_time, end_time, msg.get("statistic_id"), ) connection.send_result(msg["id"], {"statistics": statistics})
async def async_update(self): """Get the latest data and updates the states.""" # Get previous values of start and end p_start, p_end = self._period # Parse templates self.update_period() start, end = self._period # Convert times to UTC start = dt_util.as_utc(start) end = dt_util.as_utc(end) p_start = dt_util.as_utc(p_start) p_end = dt_util.as_utc(p_end) now = datetime.datetime.now() # Compute integer timestamps start_timestamp = math.floor(dt_util.as_timestamp(start)) end_timestamp = math.floor(dt_util.as_timestamp(end)) p_start_timestamp = math.floor(dt_util.as_timestamp(p_start)) p_end_timestamp = math.floor(dt_util.as_timestamp(p_end)) now_timestamp = math.floor(dt_util.as_timestamp(now)) # If period has not changed and current time after the period end... if (start_timestamp == p_start_timestamp and end_timestamp == p_end_timestamp and end_timestamp <= now_timestamp): # Don't compute anything as the value cannot have changed return await self.opp.async_add_executor_job(self._update, start, end, now_timestamp, start_timestamp, end_timestamp)
def _update_from_status_info(self, status_info): """Update the internal state from the provided information.""" self._status = status_info.status self._last_update = (dt.as_utc(status_info.last_update) if status_info.last_update else None) if status_info.last_update_successful: self._last_update_successful = dt.as_utc( status_info.last_update_successful) else: self._last_update_successful = None self._last_timestamp = status_info.last_timestamp self._total = status_info.total self._created = status_info.created self._updated = status_info.updated self._removed = status_info.removed
async def async_update(self) -> None: """Update entity.""" local = dt_util.start_of_local_day().replace(microsecond=0) start = dt_util.as_utc(local) end = start + timedelta(days=self._days) self._upcoming = await self.sonarr.calendar(start=start.isoformat(), end=end.isoformat())
def compile_statistics(instance: Recorder, start: datetime.datetime) -> bool: """Compile statistics.""" start = dt_util.as_utc(start) end = start + timedelta(hours=1) _LOGGER.debug( "Compiling statistics for %s-%s", start, end, ) platform_stats = [] for domain, platform in instance.opp.data[DOMAIN].items(): if not hasattr(platform, "compile_statistics"): continue platform_stats.append( platform.compile_statistics(instance.opp, start, end)) _LOGGER.debug("Statistics for %s during %s-%s: %s", domain, start, end, platform_stats[-1]) with session_scope( session=instance.get_session()) as session: # type: ignore for stats in platform_stats: for entity_id, stat in stats.items(): session.add( Statistics.from_stats(DOMAIN, entity_id, start, stat)) return True
def timers(self): """Get the list of added timers of the vacuum cleaner.""" return [{ "enabled": timer.enabled, "cron": timer.cron, "next_schedule": as_utc(timer.next_schedule), } for timer in self._timers]
async def get(self, request, datetime=None): """Retrieve logbook entries.""" if datetime: datetime = dt_util.parse_datetime(datetime) if datetime is None: return self.json_message("Invalid datetime", HTTP_BAD_REQUEST) else: datetime = dt_util.start_of_local_day() period = request.query.get("period") if period is None: period = 1 else: period = int(period) entity_id = request.query.get("entity") start_day = dt_util.as_utc(datetime) - timedelta(days=period - 1) end_day = start_day + timedelta(days=period) opp = request.app["opp"] def json_events(): """Fetch events and generate JSON.""" return self.json( _get_events(opp, self.config, start_day, end_day, entity_id)) return await opp.async_add_job(json_events)
def see_device(address, name, new_device=False, battery=None): """Mark a device as seen.""" if name is not None: name = name.strip("\x00") if new_device: if address in new_devices: new_devices[address]["seen"] += 1 if name: new_devices[address]["name"] = name else: name = new_devices[address]["name"] _LOGGER.debug("Seen %s %s times", address, new_devices[address]["seen"]) if new_devices[address]["seen"] < MIN_SEEN_NEW: return _LOGGER.debug("Adding %s to tracked devices", address) devs_to_track.append(address) if battery_track_interval > timedelta(0): devs_track_battery[address] = dt_util.as_utc( datetime.fromtimestamp(0) ) else: _LOGGER.debug("Seen %s for the first time", address) new_devices[address] = {"seen": 1, "name": name} return see( mac=BLE_PREFIX + address, host_name=name, source_type=SOURCE_TYPE_BLUETOOTH_LE, battery=battery, )
def async_track_point_in_utc_time(opp: OpenPeerPower, action: Callable[..., Any], point_in_time: datetime) -> CALLBACK_TYPE: """Add a listener that fires once after a specific point in UTC time.""" # Ensure point_in_time is UTC point_in_time = dt_util.as_utc(point_in_time) @callback def point_in_time_listener(event: Event) -> None: """Listen for matching time_changed events.""" now = event.data[ATTR_NOW] if now < point_in_time or hasattr(point_in_time_listener, "run"): return # Set variable so that we will never run twice. # Because the event bus might have to wait till a thread comes # available to execute this listener it might occur that the # listener gets lined up twice to be executed. This will make # sure the second time it does nothing. setattr(point_in_time_listener, "run", True) async_unsub() opp.async_run_job(action, now) async_unsub = opp.bus.async_listen(EVENT_TIME_CHANGED, point_in_time_listener) return async_unsub
async def async_zone_svc_request(self, service: dict, data: dict) -> None: """Process a service request (setpoint override) for a zone.""" if service == SVC_RESET_ZONE_OVERRIDE: await self._evo_broker.call_client_api( self._evo_device.cancel_temp_override() ) return # otherwise it is SVC_SET_ZONE_OVERRIDE temperature = max(min(data[ATTR_ZONE_TEMP], self.max_temp), self.min_temp) if ATTR_DURATION_UNTIL in data: duration = data[ATTR_DURATION_UNTIL] if duration.total_seconds() == 0: await self._update_schedule() until = dt_util.parse_datetime(self.setpoints.get("next_sp_from", "")) else: until = dt_util.now() + data[ATTR_DURATION_UNTIL] else: until = None # indefinitely until = dt_util.as_utc(until) if until else None await self._evo_broker.call_client_api( self._evo_device.set_temperature(temperature, until=until) )
def _process_timestamp(ts): """Process a timestamp into datetime object.""" if ts is None: return None if ts.tzinfo is None: return dt_util.UTC.localize(ts) return dt_util.as_utc(ts)
def _naive_time_to_utc_datetime(self, naive_time): """Convert naive time from config to utc_datetime with current day.""" # get the current local date from utc time current_local_date = (dt_util.utcnow().astimezone( dt_util.get_time_zone(self.opp.config.time_zone)).date()) # calculate utc datetime corresponding to local time return dt_util.as_utc(datetime.combine(current_local_date, naive_time))
def update(self): """Get the latest data from BOM.""" if not self.should_update(): _LOGGER.debug( "BOM was updated %s minutes ago, skipping update as" " < 35 minutes, Now: %s, LastUpdate: %s", (dt_util.utcnow() - self.last_updated), dt_util.utcnow(), self.last_updated, ) return try: result = requests.get(self._build_url(), timeout=10).json() self._data = result["observations"]["data"] # set lastupdate using self._data[0] as the first element in the # array is the latest date in the json self.last_updated = dt_util.as_utc( datetime.datetime.strptime( str(self._data[0]["local_date_time_full"]), "%Y%m%d%H%M%S")) return except ValueError as err: _LOGGER.error("Check BOM %s", err.args) self._data = None raise
def update(self): """Update probe data.""" self.api.login(self.username, self.password) _LOGGER.debug("Updating data for %s", self.device_id) try: if self.growatt_type == "total": total_info = self.api.plant_info(self.device_id) del total_info["deviceList"] # PlantMoneyText comes in as "3.1/€" remove anything that isn't part of the number total_info["plantMoneyText"] = re.sub( r"[^\d.,]", "", total_info["plantMoneyText"]) self.data = total_info elif self.growatt_type == "inverter": inverter_info = self.api.inverter_detail(self.device_id) self.data = inverter_info elif self.growatt_type == "storage": storage_info_detail = self.api.storage_params( self.device_id)["storageDetailBean"] storage_energy_overview = self.api.storage_energy_overview( self.plant_id, self.device_id) self.data = {**storage_info_detail, **storage_energy_overview} elif self.growatt_type == "mix": mix_info = self.api.mix_info(self.device_id) mix_totals = self.api.mix_totals(self.device_id, self.plant_id) mix_system_status = self.api.mix_system_status( self.device_id, self.plant_id) mix_detail = self.api.mix_detail(self.device_id, self.plant_id) # Get the chart data and work out the time of the last entry, use this as the last time data was published to the Growatt Server mix_chart_entries = mix_detail["chartData"] sorted_keys = sorted(mix_chart_entries) # Create datetime from the latest entry date_now = dt.now().date() last_updated_time = dt.parse_time(str(sorted_keys[-1])) combined_timestamp = datetime.datetime.combine( date_now, last_updated_time) # Convert datetime to UTC combined_timestamp_utc = dt.as_utc(combined_timestamp) mix_detail[ "lastdataupdate"] = combined_timestamp_utc.isoformat() # Dashboard data is largely inaccurate for mix system but it is the only call with the ability to return the combined # imported from grid value that is the combination of charging AND load consumption dashboard_data = self.api.dashboard_data(self.plant_id) # Dashboard values have units e.g. "kWh" as part of their returned string, so we remove it dashboard_values_for_mix = { # etouser is already used by the results from 'mix_detail' so we rebrand it as 'etouser_combined' "etouser_combined": dashboard_data["etouser"].replace("kWh", "") } self.data = { **mix_info, **mix_totals, **mix_system_status, **mix_detail, **dashboard_values_for_mix, } except json.decoder.JSONDecodeError: _LOGGER.error("Unable to fetch data from Growatt server")
def test_now(): """Test the now method.""" dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE)) assert abs( dt_util.as_utc(dt_util.now()).replace(tzinfo=None) - datetime.utcnow()) < timedelta(seconds=1)
def _process_timestamp(ts): """Process a timestamp into datetime object.""" if ts is None: return None if ts.tzinfo is None: return ts.replace(tzinfo=dt_util.UTC) return dt_util.as_utc(ts)
async def get(self, request, datetime=None): """Retrieve logbook entries.""" if datetime: datetime = dt_util.parse_datetime(datetime) if datetime is None: return self.json_message("Invalid datetime", HTTP_BAD_REQUEST) else: datetime = dt_util.start_of_local_day() period = request.query.get("period") if period is None: period = 1 else: period = int(period) entity_ids = request.query.get("entity") if entity_ids: try: entity_ids = cv.entity_ids(entity_ids) except vol.Invalid: raise InvalidEntityFormatError( f"Invalid entity id(s) encountered: {entity_ids}. " "Format should be <domain>.<object_id>") from vol.Invalid end_time = request.query.get("end_time") if end_time is None: start_day = dt_util.as_utc(datetime) - timedelta(days=period - 1) end_day = start_day + timedelta(days=period) else: start_day = datetime end_day = dt_util.parse_datetime(end_time) if end_day is None: return self.json_message("Invalid end_time", HTTP_BAD_REQUEST) opp = request.app["opp"] entity_matches_only = "entity_matches_only" in request.query context_id = request.query.get("context_id") if entity_ids and context_id: return self.json_message("Can't combine entity with context_id", HTTP_BAD_REQUEST) def json_events(): """Fetch events and generate JSON.""" return self.json( _get_events( opp, start_day, end_day, entity_ids, self.filters, self.entities_filter, entity_matches_only, context_id, )) return await opp.async_add_executor_job(json_events)
def test_as_utc_with_local_object(): """Test the UTC time with local object.""" dt_util.set_default_time_zone(dt_util.get_time_zone(TEST_TIME_ZONE)) localnow = dt_util.now() utcnow = dt_util.as_utc(localnow) assert localnow == utcnow assert localnow.tzinfo != utcnow.tzinfo
def state(self) -> datetime: """Return the state of the sensor.""" device = self.device time = dt_util.parse_time(device.changeableValues.nextPeriodTime) now = dt_util.utcnow() if time <= now.time(): now = now + timedelta(days=1) return dt_util.as_utc(datetime.combine(now.date(), time))
def _parse_due_date(data: dict, gmt_string) -> datetime: """Parse the due date dict into a datetime object.""" # Add time information to date only strings. if len(data["date"]) == 10: data["date"] += "T00:00:00" if dt.parse_datetime(data["date"]).tzinfo is None: data["date"] += gmt_string return dt.as_utc(dt.parse_datetime(data["date"]))
def update_from_latest_data(self) -> None: """Update the state.""" pickup_event = self.coordinator.data[0] next_pickup_event = self.coordinator.data[1] self._state = as_utc(pickup_event.date).isoformat() self._attributes.update({ ATTR_PICKUP_TYPES: async_get_pickup_type_names(self._entry, pickup_event.pickup_types), ATTR_AREA_NAME: pickup_event.area_name, ATTR_NEXT_PICKUP_TYPES: async_get_pickup_type_names(self._entry, next_pickup_event.pickup_types), ATTR_NEXT_PICKUP_DATE: as_utc(next_pickup_event.date).isoformat(), })
def _set_state(self, state, timestamp): """Set sensor state.""" if state < self._attr_state and self._sensor_name in [ "accumulated consumption", "accumulated production", "accumulated cost", ]: self._attr_last_reset = dt_util.as_utc( timestamp.replace(hour=0, minute=0, second=0, microsecond=0) ) if state < self._attr_state and self._sensor_name in [ "accumulated consumption current hour", "accumulated production current hour", ]: self._attr_last_reset = dt_util.as_utc( timestamp.replace(minute=0, second=0, microsecond=0) ) self._attr_state = state self.async_write_op_state()
def async_track_point_in_time(opp: OpenPeerPower, action: Callable[..., None], point_in_time: datetime) -> CALLBACK_TYPE: """Add a listener that fires once after a specific point in time.""" utc_point_in_time = dt_util.as_utc(point_in_time) @callback def utc_converter(utc_now: datetime) -> None: """Convert passed in UTC now to local now.""" opp.async_run_job(action, dt_util.as_local(utc_now)) return async_track_point_in_utc_time(opp, utc_converter, utc_point_in_time)
def state_updated(self, state, **kwargs): """Handle state updates.""" self._state = state if "last_reset" in kwargs: try: last_reset = dt_util.as_utc( dt_util.parse_datetime(kwargs["last_reset"])) if last_reset is None: raise ValueError self._attr_last_reset = last_reset except ValueError: _LOGGER.warning("Invalid last_reset timestamp '%s'", kwargs["last_reset"]) self.async_write_op_state()
def async_fire_time_changed(opp: OpenPeerPower, datetime_: datetime, fire_all: bool = False) -> None: """Fire a time changes event.""" opp.bus.async_fire(EVENT_TIME_CHANGED, {"now": date_util.as_utc(datetime_)}) for task in list(opp.loop._scheduled): if not isinstance(task, asyncio.TimerHandle): continue if task.cancelled(): continue mock_seconds_into_future = datetime_.timestamp() - time.time() future_seconds = task.when() - opp.loop.time() if fire_all or mock_seconds_into_future >= future_seconds: with patch( "openpeerpower.helpers.event.time_tracker_utcnow", return_value=date_util.as_utc(datetime_), ): task._run() task.cancel()
def __init__( self, tibber_home, sensor_name, device_class, unit, initial_state, state_class ): """Initialize the sensor.""" super().__init__(tibber_home) self._sensor_name = sensor_name self._model = "Tibber Pulse" self._device_name = f"{self._model} {self._home_name}" self._attr_device_class = device_class self._attr_name = f"{self._sensor_name} {self._home_name}" self._attr_state = initial_state self._attr_unique_id = f"{self._tibber_home.home_id}_rt_{self._sensor_name}" self._attr_unit_of_measurement = unit self._attr_state_class = state_class if sensor_name in [ "last meter consumption", "last meter production", ]: self._attr_last_reset = datetime.fromtimestamp(0) elif self._sensor_name in [ "accumulated consumption", "accumulated production", "accumulated cost", ]: self._attr_last_reset = dt_util.as_utc( dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0) ) elif self._sensor_name in [ "accumulated consumption current hour", "accumulated production current hour", ]: self._attr_last_reset = dt_util.as_utc( dt_util.now().replace(minute=0, second=0, microsecond=0) ) else: self._attr_last_reset = None
def _update_info(self, now=None): for person in self.service.get_all_people(): try: dev_id = "google_maps_{0}".format(slugify(person.id)) except TypeError: _LOGGER.warning("No location(s) shared with this account") return if ( self.max_gps_accuracy is not None and person.accuracy > self.max_gps_accuracy ): _LOGGER.info( "Ignoring %s update because expected GPS " "accuracy %s is not met: %s", person.nickname, self.max_gps_accuracy, person.accuracy, ) continue last_seen = dt_util.as_utc(person.datetime) if last_seen < self._prev_seen.get(dev_id, last_seen): _LOGGER.warning( "Ignoring %s update because timestamp " "is older than last timestamp", person.nickname, ) _LOGGER.debug("%s < %s", last_seen, self._prev_seen[dev_id]) continue self._prev_seen[dev_id] = last_seen attrs = { ATTR_ADDRESS: person.address, ATTR_FULL_NAME: person.full_name, ATTR_ID: person.id, ATTR_LAST_SEEN: last_seen, ATTR_NICKNAME: person.nickname, ATTR_BATTERY_CHARGING: person.charging, ATTR_BATTERY_LEVEL: person.battery_level, } self.see( dev_id=dev_id, gps=(person.latitude, person.longitude), picture=person.picture_url, source_type=SOURCE_TYPE_GPS, gps_accuracy=person.accuracy, attributes=attrs, )
def _update_from_feed(self, feed_entry, last_update, last_update_successful): """Update the internal state from the provided feed entry.""" self._title = feed_entry.title # Convert distance if not metric system. if self._unit_system == CONF_UNIT_SYSTEM_IMPERIAL: self._distance = round( IMPERIAL_SYSTEM.length(feed_entry.distance_to_home, LENGTH_KILOMETERS), 1, ) else: self._distance = round(feed_entry.distance_to_home, 1) self._latitude = round(feed_entry.coordinates[0], 5) self._longitude = round(feed_entry.coordinates[1], 5) self._attribution = feed_entry.attribution self._alert_level = feed_entry.alert_level self._activity = feed_entry.activity self._hazards = feed_entry.hazards self._feed_last_update = dt.as_utc( last_update) if last_update else None self._feed_last_update_successful = (dt.as_utc(last_update_successful) if last_update_successful else None)
async def async_set_temperature(self, **kwargs) -> None: """Set a new target temperature.""" temperature = kwargs["temperature"] until = kwargs.get("until") if until is None: if self._evo_device.setpointStatus["setpointMode"] == EVO_FOLLOW: await self._update_schedule() until = dt_util.parse_datetime(self.setpoints.get("next_sp_from", "")) elif self._evo_device.setpointStatus["setpointMode"] == EVO_TEMPOVER: until = dt_util.parse_datetime(self._evo_device.setpointStatus["until"]) until = dt_util.as_utc(until) if until else None await self._evo_broker.call_client_api( self._evo_device.set_temperature(temperature, until=until) )
async def test_if_fires_using_multiple_at(opp, calls): """Test for firing at.""" now = dt_util.now() trigger_dt = now.replace(hour=5, minute=0, second=0, microsecond=0) + timedelta(2) time_that_will_not_match_right_away = trigger_dt - timedelta(minutes=1) with patch( "openpeerpower.util.dt.utcnow", return_value=dt_util.as_utc(time_that_will_not_match_right_away), ): assert await async_setup_component( opp, automation.DOMAIN, { automation.DOMAIN: { "trigger": { "platform": "time", "at": ["5:00:00", "6:00:00"] }, "action": { "service": "test.automation", "data_template": { "some": "{{ trigger.platform }} - {{ trigger.now.hour }}" }, }, } }, ) await opp.async_block_till_done() async_fire_time_changed(opp, trigger_dt + timedelta(seconds=1)) await opp.async_block_till_done() assert len(calls) == 1 assert calls[0].data["some"] == "time - 5" async_fire_time_changed(opp, trigger_dt + timedelta(hours=1, seconds=1)) await opp.async_block_till_done() assert len(calls) == 2 assert calls[1].data["some"] == "time - 6"