async def clean_watthours_by_day( datapoints: t.AsyncIterator[DataPoint], ) -> CLEANED_DT_FLOAT: last_watthours = None last_date = None async for datapoint in datapoints: if datapoint.data is None: continue date = datapoint.collected_at.date() if date != last_date: # We have a new date watthours = ureg.Quantity(datapoint.data["magnitude"], datapoint.data["unit"]) if last_watthours is not None and last_date is not None: midpoint = datetime.datetime.combine(last_date, datetime.time(12, 0, 0)) diff = watthours - last_watthours yield midpoint, diff.to(ureg.kilowatt_hours).magnitude last_watthours = watthours last_date = datapoint.collected_at.date() if last_watthours is not None and last_date is not None: watthours = ureg.Quantity(datapoint.data["magnitude"], datapoint.data["unit"]) midpoint = datetime.datetime.combine(datapoint.collected_at.date(), datetime.time(12, 0, 0)) diff = watthours - last_watthours yield midpoint, diff.to(ureg.kilowatt_hours).magnitude
def value(self) -> t.Optional[t.Any]: if self.bt_addr is None: return None try: output: bytes = subprocess.check_output( [self.path, "-i", self.bt_addr], stderr=subprocess.STDOUT, timeout=15) except (subprocess.CalledProcessError, FileNotFoundError): return None lines = filter(None, output.split(b"\n")) found = {} for line in lines: start, value = line.rsplit(b"=", 1) start, key = start.rsplit(b" ", 1) found[key] = value try: yield_total = float(found[b"yield_total"][:-3].replace(b".", b"")) power_dc_1 = int(found[b"power_dc_1"][:-1]) power_dc_2 = int(found[b"power_dc_2"][:-1]) if power_dc_1 > 1500 or power_dc_2 > 1500: return None except (ValueError, IndexError, KeyError): return None return ureg.Quantity(yield_total, "watt")
def value(self) -> t.Optional[t.Any]: if self.bt_addr is None: raise PersistentSensorFailureError( "Inverter address not configured") try: output: bytes = subprocess.check_output( [self.path, "-i", self.bt_addr], stderr=subprocess.STDOUT, timeout=15) except subprocess.CalledProcessError as err: raise IntermittentSensorFailureError( "Failure communicating with inverter") from err except FileNotFoundError as err: raise PersistentSensorFailureError( "Inverter control software not installed") from err lines = filter(None, output.split(b"\n")) found = {} for line in lines: start, value = line.rsplit(b"=", 1) start, key = start.rsplit(b" ", 1) found[key] = value try: yield_total = float(found[b"yield_total"][:-3].replace(b".", b"")) power_dc_1 = int(found[b"power_dc_1"][:-1]) power_dc_2 = int(found[b"power_dc_2"][:-1]) if power_dc_1 > 1500 or power_dc_2 > 1500: raise IntermittentSensorFailureError( "Received corrupt data from inverter") except (ValueError, IndexError, KeyError) as err: raise IntermittentSensorFailureError( "Received incomplete data from inverter") from err return ureg.Quantity(yield_total, "watt")
async def clean_watthours_to_watts( datapoints: t.AsyncIterator[DataPoint], ) -> CLEANED_DT_FLOAT: last_watthours = None last_time = None async for datapoint in datapoints: if datapoint.data is None: continue time = datapoint.collected_at watthours = ureg.Quantity(datapoint.data["magnitude"], datapoint.data["unit"]) if last_watthours: seconds_elapsed = (time - last_time).total_seconds() time_elapsed = ureg.Quantity(seconds_elapsed, ureg.second) additional_power = watthours - last_watthours power = additional_power / time_elapsed yield time, power.to(ureg.watt).magnitude last_watthours = watthours last_time = datapoint.collected_at
def value(self) -> t.Any: try: return ureg.Quantity(self.sensor.temperature, ureg.celsius) except DataCollectionError: # This is one of our own exceptions, we don't need to re-wrap it raise except (RuntimeError, AttributeError) as err: raise IntermittentSensorFailureError( "Couldn't determine temperature") from err
def value(self) -> t.Optional[t.Any]: try: import adafruit_dht import board sensor_type = getattr(adafruit_dht, self.board) pin = getattr(board, self.pin) except (ImportError, NotImplementedError, AttributeError): # No DHT library results in an ImportError. # Running on an unknown platform results in a # NotImplementedError when getting the pin return None try: return ureg.Quantity(sensor_type(pin).temperature, ureg.celsius) except RuntimeError: return None
async def clean_watthours_to_watts( datapoints: t.AsyncIterator[DataPoint], ) -> CLEANED_DT_FLOAT: last_watthours = None last_time = None async for datapoint in datapoints: if datapoint.data is None: continue time = datapoint.collected_at if datapoint.data["unit"] == "watt_hour": watt_hours = datapoint.data["magnitude"] else: watt_hours = (ureg.Quantity(datapoint.data["magnitude"], datapoint.data["unit"]).to( ureg.watt_hour).magnitude) if last_watthours: seconds_elapsed = (time - last_time).total_seconds() hours_elapsed = seconds_elapsed / (60.0 * 60.0) additional_power = watt_hours - last_watthours power = additional_power / hours_elapsed yield time, power last_watthours = watt_hours last_time = datapoint.collected_at
def convert_temperature(magnitude: float, origin_unit: str, target_unit: str) -> float: # if origin_unit == "degC" and target_unit == "degF": # return (magnitude * 1.8) + 32 temp = ureg.Quantity(magnitude, origin_unit) return temp.to(target_unit).magnitude
def from_json_compatible(cls, json_version: t.Any) -> t.Optional[t.Any]: if json_version: return ureg.Quantity(json_version["magnitude"], ureg[json_version["unit"]]) else: return None
def from_json_compatible(cls, json_version: t.Any) -> t.Any: return ureg.Quantity(json_version["magnitude"], ureg[json_version["unit"]])
def value(self) -> t.Optional[t.Any]: try: return ureg.Quantity(self.sensor.temperature, ureg.celsius) except (RuntimeError, AttributeError): return None