Source code for neat.device

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#
# Copyright (c) 2017 Stephen Bunn (stephen@bunn.io)
# GNU GPLv3 <https://www.gnu.org/licenses/gpl-3.0.en.html>

import abc
import enum
from typing import Dict

from . import const
from .models.record import Record, RecordPoint

import pint


[docs]class AbstractDevice(object, metaclass=abc.ABCMeta): """ The base class for all valid devices. """ @abc.abstractproperty def name(self) -> str: """ The name of the device. """ raise NotImplementedError() @abc.abstractproperty def fields(self) -> Dict[str, str]: """ The expected device fields. """ raise NotImplementedError() @property def ureg(self) -> pint.UnitRegistry: """ The unit registry for all devices. """ if not hasattr(self, '_ureg'): self._ureg = pint.UnitRegistry(autoconvert_offset_to_baseunit=True) return self._ureg
[docs] def parse(self, record: Record) -> Dict[str, RecordPoint]: """ Parses a given record for the necessary device fields. :param record: The record to parse :type record: Record :returns: A dictionary of field names mapped to record points :rtype: dict """ record_parsed = {} if isinstance(record.parsed, dict) and \ not isinstance(self, UnknownDevice): for (parsed_name, parsed_config) in record.parsed.items(): record_point = record.data[parsed_config['point']] try: parsed_point = (self.ureg.Quantity( record_point.value, self.ureg.Unit(record_point.unit) )).to(self.ureg.Unit(self.fields[parsed_name])) record_parsed[parsed_name] = RecordPoint( value=parsed_point.magnitude, unit=str(parsed_point.units) ) except KeyError as exc: const.log.warning(( '`{record}` got unkown specification for a field ' '`{parsed_name}`, ignoring ...' ).format(record=record, parsed_name=parsed_name)) for (req_name, req_unit) in self.fields.items(): try: record_parsed[req_name] except KeyError as exc: const.log.warning(( '`{record}` missing required specification for parsed ' 'field `{req_name}`, setting to empty record point ...' ).format(record=record, req_name=req_name)) record_parsed[req_name] = RecordPoint( value=None, unit='dimensionless' ) return record_parsed
[docs]class UnknownDevice(AbstractDevice): """ Defines an unknown device type. .. note:: Primarily used for Obvius virtual meters """ name = 'Unknown Device' fields = {}
[docs]class PVDevice(AbstractDevice): """ Defines a photovoltaic device. """ name = 'PV Device' # TODO: determine default types for device fields = {}
[docs]class HVACDevice(AbstractDevice): """ Defines a heating, ventilation, and cooling device. """ name = 'HVAC Device' # TODO: determine default types for device fields = {}
[docs]class SolarThermalDevice(AbstractDevice): """ Defines a solar thermal device. """ name = 'Solar Thermal Device' fields = { 'energy_rate': 'btu / hour', 'flow_rate': 'gallon / minute', 'supply_temp': 'degF', 'return_temp': 'degF', 'energy_total': 'megabtu' }
[docs]class WindDevice(AbstractDevice): """ Defines a wind based device. """ name = 'Wind Device' fields = { 'inverter_real': 'kilowatt', 'inverter_energy_total': 'kilowatthour', 'rotor_speed': 'rpm', 'wind_speed': 'mph' }
[docs]class EnergyDevice(AbstractDevice): """ Defines a generic energy device. """ name = 'Energy Device' # TODO: determine default types for device fields = {}
[docs]class TemperatureDevice(AbstractDevice): """ Defines a generic temperature device. """ name = 'Temperature Device' # TODO: determine default types for device fields = {}
[docs]class DeviceType(enum.Enum): """ An enumeration of available device types. .. note:: Maps types to instances not classes """ UNKNOWN = (0x0, UnknownDevice()) PV = (0x1, PVDevice()) HVAC = (0x2, HVACDevice()) SOLAR_THERM = (0x3, SolarThermalDevice()) WIND = (0x4, WindDevice()) ENERGY = (0x5, EnergyDevice()) TEMP = (0x6, TemperatureDevice())