def _publish(self, sensor_config: SensorConfig, poller: MiFloraPoller): self.start_client() state_topic = self._get_state_topic(sensor_config) data = { MQTTAttributes.BATTERY.value: poller.parameter_value(MI_BATTERY), MQTTAttributes.TEMPERATURE.value: '{0:.1f}'.format(poller.parameter_value(MI_TEMPERATURE)), MQTTAttributes.BRIGHTNESS.value: poller.parameter_value(MI_LIGHT), MQTTAttributes.MOISTURE.value: poller.parameter_value(MI_MOISTURE), MQTTAttributes.CONDUCTIVITY.value: poller.parameter_value(MI_CONDUCTIVITY), MQTTAttributes.TIMESTAMP.value: datetime.now().isoformat(), } for key, value in data.items(): logging.debug("%s: %s", key, value) if self.config.mqtt_timestamp_format is not None: data['timestamp'] = datetime.now().strftime( self.config.mqtt_timestamp_format) json_payload = json.dumps(data) self.mqtt_client.publish(state_topic, json_payload, qos=1, retain=True) logging.info('sent data to topic %s', state_topic)
def test_name(self): """Check reading of the sensor name.""" poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.name = "my sensor name" self.assertEqual(backend.name, poller.name())
def history(mac, hostname): """Read the history from the sensor.""" global json_body global clear_hosts temp = [] poller = MiFloraPoller(mac, backend) history_list = poller.fetch_history() for entry in history_list: measurement = { "measurement": "monitor_reading", "tags": { "monitor": hostname }, "time": int(entry.wall_time.timestamp()), "fields": { "temperature": entry.temperature, "moisture": entry.moisture, "light": entry.light, "conductivity": entry.conductivity } } temp.append(measurement) if len(history_list) == len(temp) and not len(history_list) == 0: for item in temp: json_body.append(item) clear_hosts.append(hostname)
def test_read_version(self): """Test reading the version number.""" poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.set_version(1, 2, 3) self.assertEqual("1.2.3", poller.firmware_version()) self.assertEqual(0, len(backend.written_handles))
def flower_care(address): measurements = [] readings = { 'temperature': MI_TEMPERATURE, 'soil-moisture': MI_MOISTURE, 'ambient-light': MI_LIGHT, 'soil-fertility': MI_CONDUCTIVITY, 'battery': MI_BATTERY, } try: poller = MiFloraPoller(address, BluepyBackend) for key, value in readings.items(): result = poller.parameter_value(value) if result is not None: obj = Measurement(address, key, result, now) db.session.add(obj) measurements.append(obj) db.session.commit() except Exception as x: sys.stdout.write("Error with MiFlora address %s\n" % address) print(x) pass return measurements
def test_read_battery(self): """Test reading the battery level.""" poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.battery_level = 50 self.assertEqual(50, poller.battery_level()) self.assertEqual(50, poller.parameter_value(MI_BATTERY)) self.assertEqual(0, len(backend.written_handles))
def test_hight_brightness(self): """Test with negative temperature.""" poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.handle_0x35_raw = bytes( b"\xBB\x00\x00\xFF\xFF\x00\x00\x20\x0D\x03\x00\x00\x00\x00\x00\x00" ) self.assertEqual(65535, poller.parameter_value(MI_LIGHT))
def __init__(self, mac): self.poller = MiFloraPoller(mac, BluepyBackend, cache_timeout=1, retries=0) self.mac = mac name = self.poller.name()
def initialize_input(self): from miflora.miflora_poller import MiFloraPoller from btlewrap import BluepyBackend self.lock_file = '/var/lock/bluetooth_dev_hci{}'.format(self.input_dev.bt_adapter) self.sensor = MiFloraPoller( self.input_dev.location, BluepyBackend, adapter='hci{}'.format(self.input_dev.bt_adapter))
class InputModule(AbstractInput): """ A sensor support class that measures the Miflora's electrical conductivity, moisture, temperature, and light. """ def __init__(self, input_dev, testing=False): super(InputModule, self).__init__(input_dev, testing=testing, name=__name__) if not testing: from miflora.miflora_poller import MiFloraPoller from btlewrap import BluepyBackend self.lock_file = '/var/lock/bluetooth_dev_hci{}'.format( input_dev.bt_adapter) self.location = input_dev.location self.bt_adapter = input_dev.bt_adapter self.poller = MiFloraPoller(self.location, BluepyBackend, adapter='hci{}'.format( self.bt_adapter)) def get_measurement(self): """ Gets the light, moisture, and temperature """ from miflora.miflora_poller import MI_CONDUCTIVITY from miflora.miflora_poller import MI_MOISTURE from miflora.miflora_poller import MI_LIGHT from miflora.miflora_poller import MI_TEMPERATURE from miflora.miflora_poller import MI_BATTERY self.return_dict = measurements_dict.copy() self.lock_acquire(self.lock_file, timeout=3600) if self.locked[self.lock_file]: try: if self.is_enabled(0): self.value_set(0, self.poller.parameter_value(MI_BATTERY)) if self.is_enabled(1): self.value_set( 1, self.poller.parameter_value(MI_CONDUCTIVITY)) if self.is_enabled(2): self.value_set(2, self.poller.parameter_value(MI_LIGHT)) if self.is_enabled(3): self.value_set(3, self.poller.parameter_value(MI_MOISTURE)) if self.is_enabled(4): self.value_set(4, self.poller.parameter_value(MI_TEMPERATURE)) return self.return_dict finally: self.lock_release(self.lock_file)
def test_decode_measurement(self): test_data = _str2bytearray( "ce 00 00 35 00 00 00 1c c8 00 02 3c 00 fb 34 9b") s = MiFloraPoller('11:22:33') data = s._decode_measurement(test_data) assert data[MI_TEMPERATURE] == 20.6 assert data[MI_MOISTURE] == 28 assert data[MI_CONDUCTIVITY] == 200 assert data[MI_LIGHT] == 53
def test_history(self): s = MiFloraPoller('11:22:33') assert s._peripheral.history_items == 10 with s: data = s._fetch_history() assert s._peripheral.history_items == 0 assert len(s._peripheral._read_log) == 12 assert s._fetch_history() is None
def test_negative_temperature(self): """Test with negative temperature.""" poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.handle_0x35_raw = bytes( b"\x53\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x02\x3C\x00\xFB\x34\x9B" ) self.assertAlmostEqual(-17.3, poller.parameter_value(MI_TEMPERATURE), delta=0.01)
def test_no_answer_version(self): """Sensor returns None for handle 0x03. Check that this triggers the right exception. """ poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.handle_0x38_raw = None with self.assertRaises(BluetoothBackendException): poller.name()
def test_no_answer_data(self): """Sensor returns None for handle 0x35. Check that this triggers the right exception. """ poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.handle_0x35_raw = None with self.assertRaises(BluetoothBackendException): poller.parameter_value(MI_TEMPERATURE)
def init_sensors(sensor_type, sensors): sensor_type_name = sensor_type_to_name(sensor_type) if sensor_type == sensor_type_miflora: config_section = sensor_type_miflora mac_regexp = "C4:7C:8D:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}" elif sensor_type == sensor_type_mitempbt: config_section = sensor_type_mitempbt mac_regexp = "[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}" else: print_line('Unknown device type: {}'.format(sensor_type), error=True, sd_notify=True) sys.exit(1) for [name, mac] in config[config_section].items(): if not re.match(mac_regexp, mac): print_line('The MAC address "{}" seems to be in the wrong format. Please check your configuration'.format(mac), error=True, sd_notify=True) sys.exit(1) if '@' in name: name_pretty, location_pretty = name.split('@') else: name_pretty, location_pretty = name, '' name_clean = clean_identifier(name_pretty) location_clean = clean_identifier(location_pretty) sensor = dict() print('Adding sensor to device list and testing connection ...') print('Name: "{}"'.format(name_pretty)) #print_line('Attempting initial connection to Mi Flora sensor "{}" ({})'.format(name_pretty, mac), console=False, sd_notify=True) if sensor_type == sensor_type_miflora: sensor_poller = MiFloraPoller(mac=mac, backend=BluepyBackend, cache_timeout=miflora_cache_timeout, retries=3, adapter=used_adapter) elif sensor_type == sensor_type_mitempbt: sensor_poller = MiThermometerPoller(mac=mac, backend=BluepyBackend, cache_timeout=mitempbt_cache_timeout, retries=3, adapter=used_adapter) sensor['poller'] = sensor_poller sensor['name_pretty'] = name_pretty sensor['mac'] = sensor_poller._mac sensor['refresh'] = miflora_sleep_period if (sensor_type == sensor_type_miflora) else mitempbt_sleep_period sensor['location_clean'] = location_clean sensor['location_pretty'] = location_pretty sensor['stats'] = {"count": 0, "success": 0, "failure": 0} try: sensor_poller.fill_cache() sensor_poller.parameter_value(MI_BATTERY) sensor['firmware'] = sensor_poller.firmware_version() except (IOError, BluetoothBackendException, BTLEException, RuntimeError, BrokenPipeError): print_line('Initial connection to {} sensor "{}" ({}) failed.'.format(sensor_type_name, name_pretty, mac), error=True, sd_notify=True) else: print('Internal name: "{}"'.format(name_clean)) print('Device name: "{}"'.format(sensor_poller.name())) print('MAC address: {}'.format(sensor_poller._mac)) print('Firmware: {}'.format(sensor_poller.firmware_version())) print_line('Initial connection to {} sensor "{}" ({}) successful'.format(sensor_type_name, name_pretty, mac), sd_notify=True) print() sensors[name_clean] = sensor
class InputModule(AbstractInput): """ A sensor support class that measures the Miflora's electrical conductivity, moisture, temperature, and light. """ def __init__(self, input_dev, testing=False): super(InputModule, self).__init__() self.logger = logging.getLogger("mycodo.inputs.miflora") if not testing: from miflora.miflora_poller import MiFloraPoller from btlewrap import BluepyBackend self.logger = logging.getLogger("mycodo.miflora_{id}".format( id=input_dev.unique_id.split('-')[0])) self.device_measurements = db_retrieve_table_daemon( DeviceMeasurements).filter( DeviceMeasurements.device_id == input_dev.unique_id) self.location = input_dev.location self.bt_adapter = input_dev.bt_adapter self.poller = MiFloraPoller(self.location, BluepyBackend, adapter='hci{}'.format( self.bt_adapter)) def get_measurement(self): """ Gets the light, moisture, and temperature """ from miflora.miflora_poller import MI_CONDUCTIVITY from miflora.miflora_poller import MI_MOISTURE from miflora.miflora_poller import MI_LIGHT from miflora.miflora_poller import MI_TEMPERATURE from miflora.miflora_poller import MI_BATTERY return_dict = measurements_dict.copy() if self.is_enabled(0): return_dict[0]['value'] = self.poller.parameter_value(MI_BATTERY) if self.is_enabled(1): return_dict[1]['value'] = self.poller.parameter_value( MI_CONDUCTIVITY) if self.is_enabled(2): return_dict[2]['value'] = self.poller.parameter_value(MI_LIGHT) if self.is_enabled(3): return_dict[3]['value'] = self.poller.parameter_value(MI_MOISTURE) if self.is_enabled(4): return_dict[4]['value'] = self.poller.parameter_value( MI_TEMPERATURE) return return_dict
def SendInfluxMIflora(cls, InfluxDBClient, MAC, sensor_id): poller = MiFloraPoller(MAC, BluepyBackend, retries=1, cache_timeout=30) print(sensor_id) measurement = ("Flora" + str(sensor_id)) try: Flora = [{ "measurement": measurement, "fields": { "Battery": poller.parameter_value(MI_BATTERY), "Temperature": poller.parameter_value(MI_TEMPERATURE), "Light": poller.parameter_value(MI_LIGHT), "Conductivity": poller.parameter_value(MI_CONDUCTIVITY), "Moisture": poller.parameter_value(MI_MOISTURE) } }] cls.client.write_points(Flora) except BTLEException as e: print(datetime.datetime.now(), "Error connecting to Flora: error {}".format(e)) except BrokenPipeError as e: print(datetime.datetime.now(), "Error polling device. Flora" + str(MAC) + " BROKENPIPE") except Exception as e: availability = 'offline' print(availability) poller.clear_cache()
def poll(mac): """Poll data from the sensor.""" backend = _get_backend() poller = MiFloraPoller(mac, backend) print("Getting data from Mi Flora") ''' print("FW: {}".format(poller.firmware_version())) print("Name: {}".format(poller.name())) print("Temperature: {}".format(poller.parameter_value(MI_TEMPERATURE))) print("Moisture: {}".format(poller.parameter_value(MI_MOISTURE))) print("Light: {}".format(poller.parameter_value(MI_LIGHT))) print("Conductivity: {}".format(poller.parameter_value(MI_CONDUCTIVITY))) print("Battery: {}".format(poller.parameter_value(MI_BATTERY))) ''' values = {} values['name'] = poller.name() values['firmware'] = poller.firmware_version() values['temperature'] = poller.parameter_value(MI_TEMPERATURE) values['moisture'] = poller.parameter_value(MI_MOISTURE) values['light'] = poller.parameter_value(MI_LIGHT) values['conductivity'] = poller.parameter_value(MI_CONDUCTIVITY) values['battery'] = poller.parameter_value(MI_BATTERY) print('Done') return values
def _update_loop(self): try: poller = MiFloraPoller(self._bt_addr) for item in self._items: if self.get_iattr_value(item.conf, 'xiaomi_data_type') == 'temperature': item(poller.parameter_value('temperature')) elif self.get_iattr_value(item.conf, 'xiaomi_data_type') == 'light': item(poller.parameter_value(MI_LIGHT)) elif self.get_iattr_value(item.conf, 'xiaomi_data_type') == 'moisture': item(poller.parameter_value(MI_MOISTURE)) elif self.get_iattr_value( item.conf, 'xiaomi_data_type') == 'conductivity': item(poller.parameter_value(MI_CONDUCTIVITY)) elif self.get_iattr_value(item.conf, 'xiaomi_data_type') == 'battery': item(poller.parameter_value(MI_BATTERY)) elif self.get_iattr_value(item.conf, 'xiaomi_data_type') == 'name': item(poller.name()) elif self.get_iattr_value(item.conf, 'xiaomi_data_type') == 'firmware': item(poller.firmware_version()) except Exception as e: self.logger.error(str(e))
def test_rw_exception(self): """Test reaction when getting a BluetoothBackendException.""" poller = MiFloraPoller(self.TEST_MAC, RWExceptionBackend) with self.assertRaises(BluetoothBackendException): poller.firmware_version() with self.assertRaises(BluetoothBackendException): poller.name() with self.assertRaises(BluetoothBackendException): poller.parameter_value(MI_MOISTURE) with self.assertRaises(BluetoothBackendException): poller.parameter_value(MI_MOISTURE)
def history(args): """Read the history from the sensor.""" backend = _get_backend(args) print('Getting history from sensor...') poller = MiFloraPoller(args.mac, backend) history_list = poller.fetch_history() for entry in history_list: print('History from {}'.format(entry.wall_time)) print(" Temperature: {}".format(entry.temperature)) print(" Moisture: {}".format(entry.moisture)) print(" Light: {}".format(entry.light)) print(" Conductivity: {}".format(entry.conductivity))
def test_ropot(self): """Test the ropot sensor.""" poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.handle_0x35_raw = bytes( b"\xc8\x00\x00\x00\x00\x00\x00\x05\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) self.assertAlmostEqual(20, poller.parameter_value(MI_TEMPERATURE), delta=0.01) self.assertEqual(5, poller.parameter_value(MI_MOISTURE)) self.assertEqual(30, poller.parameter_value(MI_CONDUCTIVITY))
def test_invalid_data_old(self): """Check if reading of the data fails, when invalid data is received. Try this with an old version number. """ poller = MiFloraPoller(self.TEST_MAC, MockBackend) backend = self._get_backend(poller) backend.set_version(2, 5, 0) backend.override_read_handles[HANDLE_READ_SENSOR_DATA] = INVALID_DATA with self.assertRaises(BluetoothBackendException): poller.parameter_value(MI_TEMPERATURE)
def history(args): """Read the history from the sensor.""" backend = _get_backend(args) print("Getting history from sensor...") poller = MiFloraPoller(args.mac, backend) history_list = poller.fetch_history() print("History returned {} entries.".format(len(history_list))) for entry in history_list: print(f"History from {entry.wall_time}") print(f" Temperature: {entry.temperature}") print(f" Moisture: {entry.moisture}") print(f" Light: {entry.light}") print(f" Conductivity: {entry.conductivity}")
def main(argv): # default interval for sending data (seconds) INTERVAL_BETWEEN_SENDING_DATA = 15 PAR_FIELD_NAME = "name" PAR_FIELD_VALUE = "value" PAR_FIELD_NAME_INTERVAL = "interval" ##### Additional values MIFLORA_FIELD_NAME_SENSOR = "sensor" MIFLORA_FIELD_NAME_SENSOR_TYPE_TEMPERATURE = "temperature" MIFLORA_FIELD_NAME_MAC = "mac" targetSensor = MIFLORA_FIELD_NAME_SENSOR_TYPE_TEMPERATURE targetSensorMac = "" # instantiate the MBP client mbp = MBPclient() # initialize the MBP client mbp.connect() #### parse input arguments ###### paramArray = json.loads(argv[0]) #print(paramArray) for param in paramArray: if not (PAR_FIELD_NAME in param and PAR_FIELD_VALUE in param): continue elif param[PAR_FIELD_NAME].lower() == PAR_FIELD_NAME_INTERVAL.lower(): INTERVAL_BETWEEN_SENDING_DATA = int(param[PAR_FIELD_VALUE]) elif param[PAR_FIELD_NAME].lower() == MIFLORA_FIELD_NAME_SENSOR.lower( ): targetSensor = param[PAR_FIELD_VALUE] elif param[PAR_FIELD_NAME].lower() == MIFLORA_FIELD_NAME_MAC.lower(): targetSensorMac = param[PAR_FIELD_VALUE] ################################# try: # instantiate sensor reader of miflora poller = MiFloraPoller(targetSensorMac, BluepyBackend) # This loop ensures your code runs continuously to read values at a given interval while True: # retrieve sensor value received_value = poller.parameter_value(targetSensor) # send data to the MBP mbp.send_data(float(received_value)) # waits a time interval before sending new data time.sleep(INTERVAL_BETWEEN_SENDING_DATA) except: error = sys.exc_info() print('Error reading sensor value or sending to the MBP:', str(error)) # terminate the MBP client mbp.finalize()
def poll(args, backend): poller = MiFloraPoller(args.mac, backend) print("Getting data from Mi Flora") print("FW: {}".format(poller.firmware_version())) print("Name: {}".format(poller.name())) print("Temperature: {}".format(poller.parameter_value(MI_TEMPERATURE))) print("Moisture: {}".format(poller.parameter_value(MI_MOISTURE))) print("Light: {}".format(poller.parameter_value(MI_LIGHT))) print("Conductivity: {}".format(poller.parameter_value(MI_CONDUCTIVITY))) print("Battery: {}".format(poller.parameter_value(MI_BATTERY)))
def __init__(self, input_dev, testing=False): super(InputModule, self).__init__(input_dev, testing=testing, name=__name__) if not testing: from miflora.miflora_poller import MiFloraPoller from btlewrap import BluepyBackend self.lock_file = '/var/lock/bluetooth_dev_hci{}'.format(input_dev.bt_adapter) self.location = input_dev.location self.bt_adapter = input_dev.bt_adapter self.poller = MiFloraPoller( self.location, BluepyBackend, adapter='hci{}'.format(self.bt_adapter))
def GetData(): idx = 0 for mac in Parameters["Mode2"].split(','): Domoticz.Log("getting data from sensor: " + str(mac)) poller = MiFloraPoller(str(mac), BluepyBackend) Domoticz.Debug("Firmware: {}".format(poller.firmware_version())) val_bat = int("{}".format(poller.parameter_value(MI_BATTERY))) #moisture moist = (idx * 4) + 2 val_moist = "{}".format(poller.parameter_value(MI_MOISTURE)) UpdateDevice(moist, 0, val_moist, val_bat, "") #temperature temp = (idx * 4) + 3 val_temp = "{}".format(poller.parameter_value(MI_TEMPERATURE)) UpdateDevice(temp, 0, val_temp, val_bat, "") #Light lux = (idx * 4) + 4 val_lux = "{}".format(poller.parameter_value(MI_LIGHT)) UpdateDevice(lux, 0, val_lux, val_bat, "") #fertility cond = (idx * 4) + 5 val_cond = "{}".format(poller.parameter_value(MI_CONDUCTIVITY)) UpdateDevice(cond, 0, val_cond, val_bat, "") Domoticz.Debug("Data retrieved : " + str(val_moist) + "," + str(val_temp) + "," + str(val_lux) + "," + str(val_cond)) idx += 1 return
def read_plant_data(): plants = get_plant_information() all_plant_data = {} for plant in plants: try: print(plant["address"]) poller = MiFloraPoller(plant["address"], BluepyBackend) plantname = plant["name"] print_plant_data(plantname, poller) id = plant["id"] battery = "{}".format(poller.parameter_value(MI_BATTERY)) version = "{}".format(poller.firmware_version()) sunlight = "{}".format(poller.parameter_value(MI_LIGHT)) temperature = "{}".format(poller.parameter_value(MI_TEMPERATURE)) moisture = "{}".format(poller.parameter_value(MI_MOISTURE)) fertility = "{}".format(poller.parameter_value(MI_CONDUCTIVITY)) plant_data = { 'plant_id': id, 'battery': battery, 'version': version, 'sunlight': sunlight, 'temperature': temperature, 'soil_moisture': moisture, 'soil_fertility': fertility, 'timestamp': str(now) } all_plant_data.update(plant_data) except Exception as exception_message: print(exception_message) #TODO: send error status to server continue send_plant_data(plant["id"], plant_data)
def poll(args): """Poll data from the sensor.""" poller = MiFloraPoller(args.mac, BluepyBackend) print("Getting data from Mi Flora") print("FW: {}".format(poller.firmware_version())) print("Name: {}".format(poller.name())) print("Temperature: {}".format(poller.parameter_value(MI_TEMPERATURE))) print("Moisture: {}".format(poller.parameter_value(MI_MOISTURE))) print("Light: {}".format(poller.parameter_value(MI_LIGHT))) print("Conductivity: {}".format(poller.parameter_value(MI_CONDUCTIVITY))) print("Battery: {}".format(poller.parameter_value(MI_BATTERY)))