def __init__(self, config: dict) -> None: """Initialize a select input.""" self._config = config self.editable = True self._current_datetime = None initial = config.get(CONF_INITIAL) if not initial: return if self.has_date and self.has_time: current_datetime = dt_util.parse_datetime(initial) elif self.has_date: date = dt_util.parse_date(initial) current_datetime = py_datetime.datetime.combine(date, DEFAULT_TIME) else: time = dt_util.parse_time(initial) current_datetime = py_datetime.datetime.combine( py_datetime.date.today(), time) # If the user passed in an initial value with a timezone, convert it to right tz if current_datetime.tzinfo is not None: self._current_datetime = current_datetime.astimezone( dt_util.DEFAULT_TIME_ZONE) else: self._current_datetime = current_datetime.replace( tzinfo=dt_util.DEFAULT_TIME_ZONE)
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 convert_time_to_isodate(timestr: str) -> str: """Take a string like 08:00:00 and combine it with the current date.""" combined = datetime.combine(dt.start_of_local_day(), dt.parse_time(timestr)) if combined < datetime.now(): combined = combined + timedelta(days=1) return combined.isoformat()
def convert_time_to_utc(timestr): """Take a string like 08:00:00 and convert it to a unix timestamp.""" combined = datetime.combine(dt_util.start_of_local_day(), dt_util.parse_time(timestr)) if combined < datetime.now(): combined = combined + timedelta(days=1) return dt_util.as_timestamp(combined)
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_time_at_default_timezone(time_str: str) -> time | None: """Parse a time string and add default timezone.""" parsed_time = dt_util.parse_time(time_str) if parsed_time is None: return None return (dt_util.start_of_local_day().replace( hour=parsed_time.hour, minute=parsed_time.minute, second=parsed_time.second, ).timetz())
def time(value: Any) -> time_sys: """Validate and transform a time.""" if isinstance(value, time_sys): return value try: time_val = dt_util.parse_time(value) except TypeError as err: raise vol.Invalid("Not a parseable type") from err if time_val is None: raise vol.Invalid(f"Invalid time specified: {value}") return time_val
def __init__(self, config: typing.Dict) -> None: """Initialize a select input.""" self._config = config self.editable = True self._current_datetime = None initial = config.get(CONF_INITIAL) if initial: if self.has_date and self.has_time: self._current_datetime = dt_util.parse_datetime(initial) elif self.has_date: date = dt_util.parse_date(initial) self._current_datetime = datetime.datetime.combine(date, DEFAULT_TIME) else: time = dt_util.parse_time(initial) self._current_datetime = datetime.datetime.combine(DEFAULT_DATE, time)
async def async_added_to_opp(self): """Run when entity about to be added.""" await super().async_added_to_opp() # Priority 1: Initial value if self.state is not None: return default_value = py_datetime.datetime.today().strftime( "%Y-%m-%d 00:00:00") # Priority 2: Old state old_state = await self.async_get_last_state() if old_state is None: self._current_datetime = dt_util.parse_datetime(default_value) return if self.has_date and self.has_time: date_time = dt_util.parse_datetime(old_state.state) if date_time is None: current_datetime = dt_util.parse_datetime(default_value) else: current_datetime = date_time elif self.has_date: date = dt_util.parse_date(old_state.state) if date is None: current_datetime = dt_util.parse_datetime(default_value) else: current_datetime = py_datetime.datetime.combine( date, DEFAULT_TIME) else: time = dt_util.parse_time(old_state.state) if time is None: current_datetime = dt_util.parse_datetime(default_value) else: current_datetime = py_datetime.datetime.combine( py_datetime.date.today(), time) self._current_datetime = current_datetime.replace( tzinfo=dt_util.DEFAULT_TIME_ZONE)
async def async_added_to_opp(self): """Run when entity about to be added.""" await super().async_added_to_opp() # Priority 1: Initial value if self.state is not None: return # Priority 2: Old state old_state = await self.async_get_last_state() if old_state is None: self._current_datetime = dt_util.parse_datetime(DEFAULT_VALUE) return if self.has_date and self.has_time: self._current_datetime = dt_util.parse_datetime(old_state.state) elif self.has_date: date = dt_util.parse_date(old_state.state) self._current_datetime = datetime.datetime.combine(date, DEFAULT_TIME) else: time = dt_util.parse_time(old_state.state) self._current_datetime = datetime.datetime.combine(DEFAULT_DATE, time)
def is_valid_time(string: str) -> bool: """Test if string dt is a valid time.""" return dt_util.parse_time(string) is not None