def update(self): """Get the latest data from the smart plug.""" if self._last_tried is not None: last_try_s = (dt_util.now() - self._last_tried).total_seconds() / 60 retry_seconds = min(self._n_tried * 2, 10) - last_try_s if self._n_tried > 0 and retry_seconds > 0: _LOGGER.warning("Waiting %s s to retry", retry_seconds) return _state = "unknown" try: self._last_tried = dt_util.now() _state = self.smartplug.state except urllib.error.HTTPError: _LOGGER.error("D-Link connection problem") if _state == "unknown": self._n_tried += 1 self.available = False _LOGGER.warning("Failed to connect to D-Link switch") return self.state = _state self.available = True self.temperature = self.smartplug.temperature self.current_consumption = self.smartplug.current_consumption self.total_consumption = self.smartplug.total_consumption self._n_tried = 0
def update_period(self): """Parse the templates and store a datetime tuple in _period.""" start = None end = None # Parse start if self._start is not None: try: start_rendered = self._start.async_render() except (TemplateError, TypeError) as ex: HistoryStatsHelper.handle_template_exception(ex, "start") return if isinstance(start_rendered, str): start = dt_util.parse_datetime(start_rendered) if start is None: try: start = dt_util.as_local( dt_util.utc_from_timestamp( math.floor(float(start_rendered)))) except ValueError: _LOGGER.error( "Parsing error: start must be a datetime or a timestamp" ) return # Parse end if self._end is not None: try: end_rendered = self._end.async_render() except (TemplateError, TypeError) as ex: HistoryStatsHelper.handle_template_exception(ex, "end") return if isinstance(end_rendered, str): end = dt_util.parse_datetime(end_rendered) if end is None: try: end = dt_util.as_local( dt_util.utc_from_timestamp( math.floor(float(end_rendered)))) except ValueError: _LOGGER.error( "Parsing error: end must be a datetime or a timestamp") return # Calculate start or end using the duration if start is None: start = end - self._duration if end is None: end = start + self._duration if start > dt_util.now(): # History hasn't been written yet for this period return if dt_util.now() < end: # No point in making stats of the future end = dt_util.now() self._period = start, end
def _update_info(self): """Scan the network for devices. Returns boolean if scanning successful. """ _LOGGER.debug("Scanning") scanner = PortScanner() options = self._options if self.home_interval: boundary = dt_util.now() - self.home_interval last_results = [ device for device in self.last_results if device.last_update > boundary ] if last_results: exclude_hosts = self.exclude + [ device.ip for device in last_results ] else: exclude_hosts = self.exclude else: last_results = [] exclude_hosts = self.exclude if exclude_hosts: options += f" --exclude {','.join(exclude_hosts)}" try: result = scanner.scan(hosts=" ".join(self.hosts), arguments=options) except PortScannerError: return False now = dt_util.now() for ipv4, info in result["scan"].items(): if info["status"]["state"] != "up": continue name = info["hostnames"][0]["name"] if info["hostnames"] else ipv4 # Mac address only returned if nmap ran as root mac = info["addresses"].get("mac") or get_mac_address(ip=ipv4) if mac is None: _LOGGER.info("No MAC address found for %s", ipv4) continue last_results.append(Device(mac.upper(), name, ipv4, now)) self.last_results = last_results _LOGGER.debug("nmap scan successful") return True
async def test_if_action_list_weekday(opp, calls): """Test for action with a list of weekdays.""" assert await async_setup_component( opp, automation.DOMAIN, { automation.DOMAIN: { "trigger": { "platform": "event", "event_type": "test_event" }, "condition": { "condition": "time", "weekday": ["mon", "tue"] }, "action": { "service": "test.automation" }, } }, ) await opp.async_block_till_done() days_past_monday = dt_util.now().weekday() monday = dt_util.now() - timedelta(days=days_past_monday) tuesday = monday + timedelta(days=1) wednesday = tuesday + timedelta(days=1) with patch("openpeerpower.helpers.condition.dt_util.now", return_value=monday): opp.bus.async_fire("test_event") await opp.async_block_till_done() assert len(calls) == 1 with patch("openpeerpower.helpers.condition.dt_util.now", return_value=tuesday): opp.bus.async_fire("test_event") await opp.async_block_till_done() assert len(calls) == 2 with patch("openpeerpower.helpers.condition.dt_util.now", return_value=wednesday): opp.bus.async_fire("test_event") await opp.async_block_till_done() assert len(calls) == 2
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 send_message(self, message="", **kwargs): """ Build and send a message to a user. Will send plain text normally, or will build a multipart HTML message with inline image attachments if images config is defined, or will build a multipart HTML if html config is defined. """ subject = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT) data = kwargs.get(ATTR_DATA) if data: if ATTR_HTML in data: msg = _build_html_msg( message, data[ATTR_HTML], images=data.get(ATTR_IMAGES, []) ) else: msg = _build_multipart_msg(message, images=data.get(ATTR_IMAGES, [])) else: msg = _build_text_msg(message) msg["Subject"] = subject msg["To"] = ",".join(self.recipients) if self._sender_name: msg["From"] = f"{self._sender_name} <{self._sender}>" else: msg["From"] = self._sender msg["X-Mailer"] = "Open Peer Power" msg["Date"] = email.utils.format_datetime(dt_util.now()) msg["Message-Id"] = email.utils.make_msgid() return self._send_email(msg)
def update(self) -> None: """Update state.""" _LOGGER.debug("Updating sensor %s", self._name) days_left = self._smarty.filter_timer if days_left is not None and days_left != self._days_left: self._state = dt_util.now() + dt.timedelta(days=days_left) self._days_left = days_left
def state(self) -> datetime: """Return the uptime of the client.""" if self.client.uptime < 1000000000: return (dt_util.now() - timedelta(seconds=self.client.uptime)).isoformat() return dt_util.utc_from_timestamp(float( self.client.uptime)).isoformat()
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 test_set_default_time_zone(): """Test setting default time zone.""" time_zone = dt_util.get_time_zone(TEST_TIME_ZONE) dt_util.set_default_time_zone(time_zone) assert dt_util.now().tzinfo is time_zone
def get_time_until(departure_time=None): """Calculate the time between now and a train's departure time.""" if departure_time is None: return 0 delta = dt_util.utc_from_timestamp(int(departure_time)) - dt_util.now() return round(delta.total_seconds() / 60)
def now(opp: OpenPeerPower) -> datetime: """Record fetching now.""" render_info = opp.data.get(_RENDER_INFO) if render_info is not None: render_info.has_time = True return dt_util.now()
async def _schedule_notify(self): """Schedule a notification.""" delay = self._delay[self._next_delay] next_msg = now() + delay self._cancel = event.async_track_point_in_time(self.opp, self._notify, next_msg) self._next_delay = min(self._next_delay + 1, len(self._delay) - 1)
async def test_offset_in_progress_event(opp, mock_next_event): """Test that we can create an event trigger on device.""" middle_of_event = dt_util.now() + dt_util.dt.timedelta(minutes=14) end_event = middle_of_event + dt_util.dt.timedelta(minutes=60) start = middle_of_event.isoformat() end = end_event.isoformat() event_summary = "Test Event in Progress" event = copy.deepcopy(TEST_EVENT) event["start"]["dateTime"] = start event["end"]["dateTime"] = end event["summary"] = f"{event_summary} !!-15" mock_next_event.return_value.event = event assert await async_setup_component(opp, "google", {"google": GOOGLE_CONFIG}) await opp.async_block_till_done() state = opp.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == STATE_OFF assert dict(state.attributes) == { "friendly_name": TEST_ENTITY_NAME, "message": event_summary, "all_day": False, "offset_reached": True, "start_time": middle_of_event.strftime(DATE_STR_FORMAT), "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], }
async def test_all_day_offset_event(opp, mock_next_event): """Test that we can create an event trigger on device.""" tomorrow = dt_util.dt.date.today() + dt_util.dt.timedelta(days=2) end_event = tomorrow + dt_util.dt.timedelta(days=1) start = tomorrow.isoformat() end = end_event.isoformat() offset_hours = 1 + dt_util.now().hour event_summary = "Test All Day Event Offset" event = copy.deepcopy(TEST_EVENT) event["start"]["date"] = start event["end"]["date"] = end event["summary"] = f"{event_summary} !!-{offset_hours}:0" mock_next_event.return_value.event = event assert await async_setup_component(opp, "google", {"google": GOOGLE_CONFIG}) await opp.async_block_till_done() state = opp.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == STATE_OFF assert dict(state.attributes) == { "friendly_name": TEST_ENTITY_NAME, "message": event_summary, "all_day": True, "offset_reached": False, "start_time": tomorrow.strftime(DATE_STR_FORMAT), "end_time": end_event.strftime(DATE_STR_FORMAT), "location": event["location"], "description": event["description"], }
def time( before: Optional[dt_util.dt.time] = None, after: Optional[dt_util.dt.time] = None, weekday: Union[None, str, Container[str]] = None, ) -> bool: """Test if local time condition matches. Handle the fact that time is continuous and we may be testing for a period that crosses midnight. In that case it is easier to test for the opposite. "(23:59 <= now < 00:01)" would be the same as "not (00:01 <= now < 23:59)". """ now = dt_util.now() now_time = now.time() if after is None: after = dt_util.dt.time(0) if before is None: before = dt_util.dt.time(23, 59, 59, 999999) if after < before: if not after <= now_time < before: return False else: if before <= now_time < after: return False if weekday is not None: now_weekday = WEEKDAYS[now.weekday()] if (isinstance(weekday, str) and weekday != now_weekday or now_weekday not in weekday): return False return True
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 is_offset_reached(event): """Have we reached the offset time specified in the event title.""" start = get_date(event["start"]) if start is None or event["offset_time"] == dt.dt.timedelta(): return False return start + event["offset_time"] <= dt.now(start.tzinfo)
async def async_update(self): """Get the latest data and updates the states.""" now = dt_util.now() if ( not self._tibber_home.last_data_timestamp or (self._tibber_home.last_data_timestamp - now).total_seconds() < 5 * 3600 + self._spread_load_constant or not self.available ): _LOGGER.debug("Asking for new data") await self._fetch_data() elif ( self._tibber_home.current_price_total and self._last_updated and self._last_updated.hour == now.hour and self._tibber_home.last_data_timestamp ): return res = self._tibber_home.current_price_data() self._attr_state, price_level, self._last_updated = res self._attr_extra_state_attributes["price_level"] = price_level attrs = self._tibber_home.current_attributes() self._attr_extra_state_attributes.update(attrs) self._attr_available = self._attr_state is not None self._attr_unit_of_measurement = self._tibber_home.price_unit
async def async_update(self) -> None: """Get the latest state data.""" next_sp_from = self._setpoints.get("next_sp_from", "2000-01-01T00:00:00+00:00") if dt_util.now() >= dt_util.parse_datetime(next_sp_from): await self._update_schedule() # no schedule, or it's out-of-date self._device_state_attrs = {"setpoints": self.setpoints}
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
async def _update_consumption_data(self, *_) -> None: """Update water consumption data from the API.""" today = dt_util.now().date() start_date = datetime(today.year, today.month, today.day, 0, 0) end_date = datetime(today.year, today.month, today.day, 23, 59, 59, 999000) self._water_usage = await self.api_client.water.get_consumption_info( self._flo_location_id, start_date, end_date ) LOGGER.debug("Updated Flo consumption data: %s", self._water_usage)
def due_in_minutes(timestamp): """Get the time in minutes from a timestamp. The timestamp should be in the format day.month.year hour:minute """ diff = datetime.strptime( timestamp, "%d.%m.%y %H:%M") - dt_util.now().replace(tzinfo=None) return int(diff.total_seconds() // 60)
def forecast(self): """Return the forecast array.""" if not self._forecast: return [] if self._mode == "hourly": forecast_filtered = [ x for x in self._forecast if x.forecasted_hours == 1 and parse_datetime(x.forecast_date) > (now().utcnow() - timedelta(hours=1)) ] fcdata_out = [{ ATTR_FORECAST_TIME: data_in.forecast_date, ATTR_FORECAST_CONDITION: next( (k for k, v in CONDITION_CLASSES.items() if int(data_in.weather_type) in v), None, ), ATTR_FORECAST_TEMP: float(data_in.feels_like_temperature), ATTR_FORECAST_PRECIPITATION_PROBABILITY: (int(float(data_in.precipitation_probability)) if int(float(data_in.precipitation_probability)) >= 0 else None), ATTR_FORECAST_WIND_SPEED: data_in.wind_strength, ATTR_FORECAST_WIND_BEARING: data_in.wind_direction, } for data_in in forecast_filtered] else: forecast_filtered = [ f for f in self._forecast if f.forecasted_hours == 24 ] fcdata_out = [{ ATTR_FORECAST_TIME: data_in.forecast_date, ATTR_FORECAST_CONDITION: next( (k for k, v in CONDITION_CLASSES.items() if int(data_in.weather_type) in v), None, ), ATTR_FORECAST_TEMP_LOW: data_in.min_temperature, ATTR_FORECAST_TEMP: data_in.max_temperature, ATTR_FORECAST_PRECIPITATION_PROBABILITY: data_in.precipitation_probability, ATTR_FORECAST_WIND_SPEED: data_in.wind_strength, ATTR_FORECAST_WIND_BEARING: data_in.wind_direction, } for data_in in forecast_filtered] return fcdata_out
def set_time(self): """Set device time.""" # Calling this clears any local temperature override and # reverts to the scheduled temperature. now = dt_util.now() self.device.time = { "day": now.weekday(), "hour": now.hour, "minute": now.minute, }
def setup_platform(opp, config, add_entities, discovery_info=None): """Set up the Workday sensor.""" add_holidays = config.get(CONF_ADD_HOLIDAYS) remove_holidays = config.get(CONF_REMOVE_HOLIDAYS) country = config[CONF_COUNTRY] days_offset = config[CONF_OFFSET] excludes = config[CONF_EXCLUDES] province = config.get(CONF_PROVINCE) sensor_name = config[CONF_NAME] workdays = config[CONF_WORKDAYS] year = (get_date(dt.now()) + timedelta(days=days_offset)).year obj_holidays = getattr(holidays, country)(years=year) if province: # 'state' and 'prov' are not interchangeable, so need to make # sure we use the right one if hasattr(obj_holidays, "PROVINCES") and province in obj_holidays.PROVINCES: obj_holidays = getattr(holidays, country)(prov=province, years=year) elif hasattr(obj_holidays, "STATES") and province in obj_holidays.STATES: obj_holidays = getattr(holidays, country)(state=province, years=year) else: _LOGGER.error("There is no province/state %s in country %s", province, country) return # Add custom holidays try: obj_holidays.append(add_holidays) except TypeError: _LOGGER.debug("No custom holidays or invalid holidays") # Remove holidays try: for date in remove_holidays: obj_holidays.pop(date) except TypeError: _LOGGER.debug("No holidays to remove or invalid holidays") _LOGGER.debug("Found the following holidays for your configuration:") for date, name in sorted(obj_holidays.items()): _LOGGER.debug("%s %s", date, name) add_entities( [ IsWorkdaySensor(obj_holidays, workdays, excludes, days_offset, sensor_name) ], True, )
async def test_if_action_after(opp, calls): """Test for if action after.""" assert await async_setup_component( opp, automation.DOMAIN, { automation.DOMAIN: { "trigger": { "platform": "event", "event_type": "test_event" }, "condition": { "condition": "time", "after": "10:00" }, "action": { "service": "test.automation" }, } }, ) await opp.async_block_till_done() before_10 = dt_util.now().replace(hour=8) after_10 = dt_util.now().replace(hour=14) with patch("openpeerpower.helpers.condition.dt_util.now", return_value=before_10): opp.bus.async_fire("test_event") await opp.async_block_till_done() assert len(calls) == 0 with patch("openpeerpower.helpers.condition.dt_util.now", return_value=after_10): opp.bus.async_fire("test_event") await opp.async_block_till_done() assert len(calls) == 1
def __init__(self): """Set the event data.""" middle_of_event = dt_util.now() - dt_util.dt.timedelta(minutes=30) self.event = { "start": { "dateTime": middle_of_event.isoformat() }, "end": { "dateTime": (middle_of_event + dt_util.dt.timedelta(minutes=60)).isoformat() }, "summary": "Current Event", }
def __init__(self): """Set the event to a future event.""" one_hour_from_now = dt_util.now() + dt_util.dt.timedelta(minutes=30) self.event = { "start": { "dateTime": one_hour_from_now.isoformat() }, "end": { "dateTime": (one_hour_from_now + dt_util.dt.timedelta(minutes=60)).isoformat() }, "summary": "Future Event", }
def extra_state_attributes(self): """Return the device state attributes.""" if self._type == 1 and self._state is not None: schedule = { TIME_FRAME1_BEGIN: None, TIME_FRAME1_END: None, TIME_FRAME2_BEGIN: None, TIME_FRAME2_END: None, TIME_FRAME3_BEGIN: None, TIME_FRAME3_END: None, } time_frame = self._state.split(";") for index, item in enumerate(sorted(schedule.items())): if index < len(time_frame): parsed = datetime.datetime.strptime( time_frame[index], "%H:%M") parsed = parsed.replace(dt_util.now().year, dt_util.now().month, dt_util.now().day) schedule[item[0]] = parsed.isoformat() return schedule return None