예제 #1
0
 def __parse(sml_frame: SmlFrame) -> tp.Optional[Sample]:
     """
     Internal helper to extract relevant information
     :param sml_frame: SmlFrame from parser
     """
     sample = None
     for sml_mesage in sml_frame:
         if 'messageBody' in sml_mesage:
             sml_list: tp.List[SmlListEntry] = sml_mesage[
                 'messageBody'].get('valList', [])
             for sml_entry in sml_list:
                 if sample is None:
                     sample = Sample()
                 obis_code: str = sml_entry.get('objName', '')
                 value = sml_entry.get('value', '')
                 # Differentiate SML Messages based on whether they contain a unit
                 if 'unit' in sml_entry:
                     sample.channels.append(
                         ChannelValue(obis_code, value,
                                      sml_entry.get('unit')))
                 else:
                     # Determine the meter_id from OBIS code
                     if obis_code in SmlReader.OBIS_CODES_METER_ID:
                         sample.meter_id = value
                     # Add Channels without unit
                     else:
                         sample.channels.append(
                             ChannelValue(obis_code, value))
     return sample
예제 #2
0
 def __parse(response: str) -> tp.Optional[Sample]:
     """
     Internal helper to extract relevant information
     :param response: decoded line
     """
     parsed = None
     for ident, value, unit in re.findall(r"([\d.]+)\(([\d.]+)\*?([\w\d.]+)?\)", response):
         if parsed is None:
             parsed = Sample()
         if not unit and ident == "9.21":
             parsed.meter_id = value
         else:
             parsed.channels.append(ChannelValue(ident, float(value), unit))
     return parsed
예제 #3
0
 def __parse(self, response) -> Sample:
     """
     Internal helper to extract relevant information
     :param sml_frame: sml data from parser
     """
     parsed = Sample()
     for ident, value, unit in re.findall(
             r"([\d.]+)\(([\d.]+)\*?([\w\d.]+)?\)", response):
         if not unit:
             if strip(self.meter_id) in value:
                 parsed.meter_id = value
         else:
             parsed.channels.append({
                 'objName': ident,
                 'value': float(value),
                 'unit': unit
             })
     return parsed
예제 #4
0
 def __parse(self, sml_frame: tp.Union[list, dict], parsed=None) -> Sample:
     """
     Internal helper to extract relevant information
     :param sml_frame: sml data from parser
     :param parsed: only for recursive object reference forwarding
     """
     if parsed is None:
         parsed = Sample()
     if isinstance(sml_frame, list):
         for elem in sml_frame:
             self.__parse(elem, parsed)
     elif isinstance(sml_frame, dict):
         if 'messageBody' in sml_frame:
             var_list = sml_frame['messageBody'].get('valList', [])
             for variable in var_list:
                 if 'unit' not in variable and strip(
                         self.meter_id) in strip(
                             str(variable.get('value', ''))):
                     parsed.meter_id = variable.get('value')
                     break
             if parsed.meter_id:
                 parsed.channels.extend(var_list)
     return parsed
예제 #5
0
                'pressure': {
                    'uuid': '250ca04a-02ee-4a1b-98dd-3423b21008b7',
                    'interval': '1h'
                }
            },
            'id': 118,
            'protocol': 'BME280'
        }
    },
    'middleware': {
        'type': 'volkszaehler',
        'middleware_url': 'http://localhost/middleware.php'
    }
}

SAMPLE_BME = Sample()
SAMPLE_BME.meter_id = '0x76'
SAMPLE_BME.channels = [{
    'objName': 'TEMPERATURE',
    'value': 20.0,
    'unit': 'C'
}, {
    'objName': 'HUMIDITY',
    'value': 50.0,
    'unit': '%'
}, {
    'objName': 'PRESSURE',
    'value': 1000.0,
    'unit': 'hPa'
}]
예제 #6
0
                'pressure': {
                    'uuid': '250ca04a-02ee-4a1b-98dd-3423b21008b7',
                    'interval': '1h'
                }
            },
            'id': 118,
            'protocol': 'BME280'
        }
    },
    'middleware': {
        'type': 'volkszaehler',
        'middleware_url': 'http://localhost/middleware.php'
    }
}

SAMPLE_BME = Sample()
SAMPLE_BME.meter_id = '0x76'
SAMPLE_BME.channels = [
    ChannelValue('TEMPERATURE', 20.0, 'C'),
    ChannelValue('HUMIDITY', 50.0, '%'),
    ChannelValue('PRESSURE', 1000.0, 'hPa')
]

SAMPLE_SML = Sample()
SAMPLE_SML.meter_id = '1 EMH00 12345678'
SAMPLE_SML.channels = [ChannelValue('1.8.0*255', 10000, 'kWh')]

SAMPLE_PLAIN = Sample()
SAMPLE_PLAIN.meter_id = '888777666'
SAMPLE_PLAIN.channels = [ChannelValue('6.8', 20000, 'kWh')]
예제 #7
0
    def poll(self) -> tp.Optional[Sample]:
        """
        Poll device
        :return: True, if successful
        """
        # pylint: disable=too-many-locals, too-many-statements
        sample = Sample()
        with self.__I2C_LOCK:
            try:
                bus = smbus.SMBus(1)
                # Register Addresses
                reg_data = 0xF7
                reg_control = 0xF4

                reg_control_hum = 0xF2

                # Oversample setting - page 27
                oversample_temp = 2
                oversample_pres = 2
                mode = 1

                # Oversample setting for humidity register - page 26
                oversample_hum = 2
                bus.write_byte_data(self.meter_id, reg_control_hum,
                                    oversample_hum)

                control = oversample_temp << 5 | oversample_pres << 2 | mode
                bus.write_byte_data(self.meter_id, reg_control, control)

                # Read blocks of calibration data from EEPROM
                # See Page 22 data sheet
                cal1 = bus.read_i2c_block_data(self.meter_id, 0x88, 24)
                cal2 = bus.read_i2c_block_data(self.meter_id, 0xA1, 1)
                cal3 = bus.read_i2c_block_data(self.meter_id, 0xE1, 7)

                dig = COMP_PARAM_STRUCT.parse(
                    bytes(cal1) + bytes(cal2) + bytes(cal3))
                dig2 = AUX_STRUCT.parse(
                    bytes([
                        dig.bitfield.n1 << 4 | dig.bitfield.n2,
                        dig.bitfield.n4 << 4 | dig.bitfield.n5,
                        dig.bitfield.n6 << 4 | dig.bitfield.n3
                    ]))

                # Wait in ms (Datasheet Appendix B: Measurement time and current calculation)
                wait_time = 1.25 + 2.3 * oversample_temp + 2.3 * oversample_pres + 0.575 \
                            + 2.3 * oversample_hum + 0.575
                time.sleep(wait_time / 1000)  # Wait the required time

                # Read temperature/pressure/humidity
                data = bus.read_i2c_block_data(self.meter_id, reg_data, 8)
                bus.close()
                pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
                temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
                hum_raw = (data[6] << 8) | data[7]

                # Refine temperature
                var1 = (((temp_raw >> 3) - (dig['T1'] << 1)) * dig['T2']) >> 11
                var2 = (((((temp_raw >> 4) - dig['T1']) * (
                    (temp_raw >> 4) - dig['T1'])) >> 12) * dig['T3']) >> 14
                t_fine = var1 + var2
                temperature = float(((t_fine * 5) + 128) >> 8)
                sample.channels.append({
                    'objName': 'TEMPERATURE',
                    'value': temperature / 100.0,
                    'unit': '°C'
                })

                # Refine pressure and adjust for temperature
                var1 = t_fine / 2.0 - 64000.0
                var2 = var1 * var1 * dig['P6'] / 32768.0
                var2 = var2 + var1 * dig['P5'] * 2.0
                var2 = var2 / 4.0 + dig['P4'] * 65536.0
                var1 = (dig['P3'] * var1 * var1 / 524288.0 +
                        dig['P2'] * var1) / 524288.0
                var1 = (1.0 + var1 / 32768.0) * dig['P1']
                if var1 == 0:
                    pressure = 0
                else:
                    pressure = 1048576.0 - pres_raw
                    pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
                    var1 = dig['P9'] * pressure * pressure / 2147483648.0
                    var2 = pressure * dig['P8'] / 32768.0
                    pressure = pressure + (var1 + var2 + dig['P7']) / 16.0
                sample.channels.append({
                    'objName': 'PRESSURE',
                    'value': pressure / 100.0,
                    'unit': 'hPa'
                })

                # Refine humidity
                humidity = t_fine - 76800.0
                humidity = (
                    (hum_raw -
                     (dig2['H4'] * 64.0 + dig2['H5'] / 16384.0 * humidity)) *
                    (dig['H2'] / 65536.0 *
                     (1.0 + dig['H6'] / 67108864.0 * humidity *
                      (1.0 + dig['H3'] / 67108864.0 * humidity))))
                humidity = humidity * (1.0 - dig['H1'] * humidity / 524288.0)
                if humidity > 100:
                    humidity = 100
                elif humidity < 0:
                    humidity = 0
                sample.channels.append({
                    'objName': 'HUMIDITY',
                    'value': humidity,
                    'unit': '%'
                })
            except OSError as err:
                if isinstance(err, PermissionError):
                    error(
                        "Bme280Reader: Insufficient permissions to access I2C bus."
                    )
                else:
                    error(
                        f"Bme280Reader: Cannot detect BME280 at add {self.meter_id:02x}"
                    )
        return sample
예제 #8
0
 def _fetch_untracked(self) -> tp.Optional[Sample]:
     # pylint: disable=too-many-locals
     try:
         with Bme280Reader.I2C_BUS_LOCK:
             with SMBus(self.i2c_bus) as bus:
                 # Read Chip ID
                 chip_id = bus.read_byte_data(self.i2c_address, Bme280Reader.REG_ADDR_CHIP_ID)
                 if self.__calibration_data is None or not self.cache_calibration:
                     self.__calibration_data = self.__read_calibration_data(bus)
                 else:
                     logger.debug("Using cached calibration data")
                 calibration_data = self.__calibration_data
                 # Reconfigure sensor
                 if self.__reconfiguration_required or self.mode is Bme280SensorMode.FORCED:
                     # Reset sensor to sleep mode for reconfiguration
                     self.__reset(bus)
                     logger.debug("Reconfiguring sensor")
                     # Configure humidity
                     self.__set_register_ctrl_hum(bus, self.humidity_oversampling)
                     # Configure other measurement parameters
                     self.__set_register_config(bus, self.standby_time, self.irr_filter_coefficient)
                     # Activate configuration
                     self.__set_register_ctrl_meas(bus, self.mode, self.temperature_oversampling,
                                                   self.pressure_oversampling)
                     self.__reconfiguration_required = False
                 # Wait for the measurement if running in forced mode
                 if self.mode is Bme280SensorMode.FORCED:
                     logger.debug("Waiting for measurement to complete in forced mode")
                     osrs_t_time = 2.3 * self.temperature_oversampling
                     osrs_p_time = 2.3 * self.pressure_oversampling + 0.575
                     osrs_h_time = 2.3 * self.humidity_oversampling + 0.575
                     measurement_time = 1.25 + osrs_t_time + osrs_p_time + osrs_h_time
                     # Wait for measurement to complete
                     time.sleep(measurement_time / 1000)
                     # Read measuring status
                     measuring, _ = self.__read_status(bus)
                     if measuring:
                         logger.error("Measurement is still in progress after maximum measurement time! Aborting...")
                         return None
                 # Read measurement registers
                 logger.debug("Reading measurement registers")
                 measurement = bus.read_i2c_block_data(self.i2c_address, Bme280Reader.REG_ADDR_MEASUREMENT_START, 8)
         # Parse measurement
         logger.debug("Parsing measurement")
         measurement_container = Bme280Reader.STRUCT_MEASUREMENT.parse(bytes(measurement))
         # Calculate fine temperature to enable temperature compensation for the other measurements
         fine_temperature = Bme280Reader.calculate_fine_temperature(calibration_data, measurement_container.temp_raw)
         # Calculate measurement results
         temperature = Bme280Reader.calculate_temperature(fine_temperature)
         pressure = Bme280Reader.calculate_pressure(calibration_data, measurement_container.press_raw,
                                            fine_temperature)
         humidity = Bme280Reader.calculate_humidity(calibration_data, measurement_container.hum_raw,
                                                    fine_temperature)
         # Determine meter_id
         meter_id = Bme280Reader.derive_meter_id(calibration_data, chip_id)
         # Return Sample
         return Sample(meter_id=meter_id, channels=[ChannelValue('TEMPERATURE', temperature, '°C'),
                                                    ChannelValue('PRESSURE', pressure, 'Pa'),
                                                    ChannelValue('HUMIDITY', humidity, '%')])
     except OSError as err:
         logger.error(f"Accessing the smbus faild: {err}")
     except ConstructError as err:
         logger.error(f"Parsing the binary data failed: {err}")
     return None