def __save_sensor_db(p_id='', p_type='', value_list=None): if not value_list: value_list = [] record = models.Sensor(address=p_id) assert isinstance(record, models.Sensor) zone_sensor = models.ZoneSensor.query.filter_by( sensor_address=p_id).first() if zone_sensor: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = '(not defined) ' + p_id record.updated_on = utils.get_base_location_now_date() record.type = p_type if 'Humidity' in value_list: record.humidity = utils.round_sensor_value(value_list['Humidity']) if 'Temperature' in value_list: record.temperature = utils.round_sensor_value( value_list['Temperature']) if 'Battery numeric' in value_list: record.battery_level = value_list['Battery numeric'] if 'Rssi numeric' in value_list: record.rssi = value_list['Rssi numeric'] current_record = models.Sensor.query.filter_by(address=p_id).first() record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=True, save_to_graph=True, ignore_only_updated_on_change=True)
def check_inactive(): """check for inactive sensors not read recently but in database""" sensor_list = models.Sensor().query_all() defined_sensor_list = models.ZoneSensor().query_all() ref_list = [] delta = (datetime.datetime.now() - P.last_warning).total_seconds() log_warn = (delta > 60 * 15) for zone_sensor in defined_sensor_list: ref_list.append(zone_sensor.sensor_address) for sensor in sensor_list: elapsed = round((utils.get_base_location_now_date() - sensor.updated_on).total_seconds() / 60, 0) if sensor.address not in ref_list: L.l.warning('Sensor {} {} not defined'.format( sensor.address, sensor.sensor_name)) #current_record = models.SensorError.query.filter_by(sensor_address=sensor.address).first() #record = models.SensorError() #record.sensor_name = sensor.sensor_name #if current_record is not None: # record.error_count = current_record.error_count #else: # record.error_count = 0 #record.error_count += 1 #record.error_type = 0 #record.save_changed_fields(current_record=None, new_record=record, save_to_graph=True, save_all_fields=True) if log_warn and elapsed > 2 * P.sampling_period_seconds: L.l.warning('Sensor {} type {} not updated since {} min'.format( sensor.sensor_name, sensor.type, elapsed)) P.last_warning = datetime.datetime.now()
def record_update(obj): # save sensor state to db, except for current node try: sensor_host_name = utils.get_object_field_value(obj, 'name') L.l.debug('Received sensor state update from {}'.format(sensor_host_name)) # avoid node to update itself in infinite recursion if sensor_host_name != Constant.HOST_NAME: address = utils.get_object_field_value(obj, 'address') n_address = utils.get_object_field_value(obj, 'n_address') sensor_type = utils.get_object_field_value(obj, 'type') record = models.Sensor(address=address) assert isinstance(record, models.Sensor) zone_sensor = models.ZoneSensor.query.filter_by(sensor_address=address).first() if zone_sensor is not None: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = '(n/a) {} {} {}'.format(address, n_address, sensor_type) record.type = utils.get_object_field_value(obj, 'type') record.updated_on = utils.get_base_location_now_date() if obj.has_key('counters_a'): record.counters_a = utils.get_object_field_value(obj, 'counters_a') if obj.has_key('counters_b'): record.counters_b = utils.get_object_field_value(obj, 'counters_b') if obj.has_key('delta_counters_a'): record.delta_counters_a = utils.get_object_field_value(obj, 'delta_counters_a') if obj.has_key('delta_counters_b'): record.delta_counters_b = utils.get_object_field_value(obj, 'delta_counters_b') if obj.has_key('temperature'): record.temperature = utils.get_object_field_value(obj, 'temperature') if obj.has_key('humidity'): record.humidity = utils.get_object_field_value(obj, 'humidity') if obj.has_key('iad'): record.iad = utils.get_object_field_value(obj, 'iad') if obj.has_key('vad'): record.vad = utils.get_object_field_value(obj, 'vad') if obj.has_key('vdd'): record.vdd = utils.get_object_field_value(obj, 'vdd') if obj.has_key('pio_a'): record.pio_a = utils.get_object_field_value(obj, 'pio_a') if obj.has_key('pio_b'): record.pio_b = utils.get_object_field_value(obj, 'pio_b') if obj.has_key('sensed_a'): record.sensed_a = utils.get_object_field_value(obj, 'sensed_a') if obj.has_key('sensed_b'): record.sensed_b = utils.get_object_field_value(obj, 'sensed_b') current_record = models.Sensor.query.filter_by(address=address).first() # force field changed detection for delta_counters if current_record: current_record.delta_counters_a = 0 current_record.delta_counters_b = 0 record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=False, save_to_graph=False) # commit() # not needed? # enable below only for testing on netbook # if Constant.HOST_NAME == 'xxxnetbook' and (record.delta_counters_a or record.delta_counters_b): # dispatcher.send(Constant.SIGNAL_UTILITY, sensor_name=record.sensor_name, # units_delta_a=record.delta_counters_a, # units_delta_b=record.delta_counters_b, total_units_a=record.counters_a, # total_units_b=record.counters_b, # sampling_period_seconds=owsensor_loop.sampling_period_seconds) except Exception as ex: L.l.error('Error on sensor update, err {}'.format(ex), exc_info=True) db.session.rollback()
def rule_sensor_temp_extreme(obj=models.Sensor(), field_changed_list=None): if hasattr(obj, 'temperature') and obj.temperature is not None: m = models.ZoneSensor zonesensor = m().query_filter_first(m.sensor_name == obj.sensor_name) if zonesensor is not None and zonesensor.target_material is not None: m = models.Zone zone = m().query_filter_first(m.id == zonesensor.zone_id) if zone is not None: max = min = None if zone.is_indoor: location = 'indoor' elif zone.is_indoor_heated: location = 'indoor_heated' elif zone.is_outdoor: location = 'outdoor' elif zone.is_outdoor_heated: location = 'outdoor_heated' else: L.l.warning( "Zone {} has no indoor/outdoor location set".format( zone.name)) return False max_temp = TempStore.max_temp[location] if max_temp.has_key(zonesensor.target_material): max = max_temp[zonesensor.target_material] else: L.l.warning("Unknown max target material {}".format( zonesensor.target_material)) min_temp = TempStore.min_temp[location] if min_temp.has_key(zonesensor.target_material): min = min_temp[zonesensor.target_material] else: L.l.warning("Unknown min target material {}".format( zonesensor.target_material)) if max is not None and obj.temperature >= max: rule_common.notify_via_all( title="Max temperature reached for {} is {}".format( obj.sensor_name, obj.temperature), message="!", priority=1) if min is not None and obj.temperature <= min: rule_common.notify_via_all( title="Min temperature reached for {} is {}".format( obj.sensor_name, obj.temperature), message="!", priority=1) else: L.l.warning( "Cannot find a zone for zone_sensor {}".format(zonesensor)) return True
def rule_openhab_sensor(obj=models.Sensor(), field_changed_list=None): key = 'temperature' if hasattr(obj, key) and obj.temperature is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.temperature) key = 'humidity' if hasattr(obj, key) and obj.humidity is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.humidity) key = 'pressure' if hasattr(obj, key) and obj.pressure is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.pressure) key = 'vad' if hasattr(obj, key) and obj.vad is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.vad) key = 'vdd' if hasattr(obj, key) and obj.vdd is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.vdd) key = 'iad' if hasattr(obj, key) and obj.iad is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.iad)
def thread_run(): if not P.initialised: init_solar_aps() if P.initialised: try: production = utils.parse_http( model_helper.get_param(Constant.P_SOLAR_APS_LOCAL_URL), P.start_keyword, P.end_keyword) last_power = utils.parse_http( model_helper.get_param(Constant.P_SOLAR_APS_LOCAL_URL), P.start_keyword_now, P.end_keyword_now) temperature = utils.parse_http(model_helper.get_param( Constant.P_SOLAR_APS_LOCAL_REALTIME_URL), P.start_key_temp, P.end_key_temp, end_first=True) panel_id = utils.parse_http(model_helper.get_param( Constant.P_SOLAR_APS_LOCAL_REALTIME_URL), P.start_key_panel, P.end_key_panel, end_first=True) utility_name = model_helper.get_param( Constant.P_SOLAR_UTILITY_NAME) if temperature is not None: zone_sensor = models.ZoneSensor.query.filter_by( sensor_address=panel_id).first() if zone_sensor is None: L.l.warning( 'Solar panel id {} is not defined in zone sensor list'. format(panel_id)) record = models.Sensor(address=panel_id) current_record = models.Sensor.query.filter_by( address=panel_id).first() record.type = 'solar' if current_record is not None: record.sensor_name = current_record.sensor_name else: if zone_sensor: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = record.type + panel_id record.temperature = temperature record.updated_on = utils.get_base_location_now_date() # fixme: keeps saving same temp even when panels are off. stop during night. record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=True, save_to_graph=True, debug=False) if production is not None: production = float(production) record = models.Utility() record.utility_name = utility_name current_record = models.Utility.query.filter_by( utility_name=utility_name).first() if current_record is not None: if current_record.units_total is None: record.units_delta = 0 else: record.units_delta = production - current_record.units_total if record.units_delta == 0: # do not waste db space if no power generated return record.units_total = production record.unit_name = current_record.unit_name record.units_2_delta = last_power record.unit_2_name = current_record.unit_2_name else: record.units_delta = production record.units_total = production if current_record.unit_cost is None: current_record.unit_cost = 0.0 record.cost = 1.0 * record.units_delta * current_record.unit_cost record.save_changed_fields(current_record=current_record, new_record=record, debug=False, notify_transport_enabled=True, save_to_graph=True, save_all_fields=False) except Exception as ex: L.l.warning("Got exception on solar thread run, ex={}".format(ex))
def mqtt_on_message(client, userdata, msg): # L.l.info("Topic={} payload={}".format(msg.topic, msg.payload)) if '/SENSOR' in msg.topic or '/RESULT' in msg.topic: topic_clean = P.sonoff_topic.replace('#', '') if topic_clean in msg.topic: sensor_name = msg.topic.split(topic_clean)[1].split('/')[1] obj = utils.json2obj(msg.payload) if 'ENERGY' in obj: energy = obj['ENERGY'] power = float(energy['Power']) if 'Voltage' in energy: voltage = int(energy['Voltage']) else: voltage = None if 'Factor' in energy: factor = energy['Factor'] else: factor = None if 'Current' in energy: current = float(energy['Current']) else: current = None # unit should match Utility unit name in models definition dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=power, unit='watt') # todo: save total energy utility if voltage or factor or current: zone_sensor = models.ZoneSensor.query.filter_by(sensor_address=sensor_name).first() if zone_sensor is not None: current_record = models.Sensor.query.filter_by(address=sensor_name).first() if current_record is None: pass else: current_record.vad = None current_record.iad = None current_record.vdd = None record = models.Sensor(address=sensor_name, sensor_name=zone_sensor.sensor_name) record.is_event_external = True if voltage is not None: record.vad = round(voltage, 0) record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=True, save_to_graph=True, debug=False) if current is not None: record.iad = round(current, 1) record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=True, save_to_graph=True, debug=False) if factor is not None: record.vdd = round(factor, 1) record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=True, save_to_graph=True, debug=False) # dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=current, unit='kWh') elif 'POWER' in obj: power_is_on = obj['POWER'] == 'ON' current_relay = models.ZoneCustomRelay.query.filter_by(gpio_pin_code=sensor_name, gpio_host_name=Constant.HOST_NAME).first() if current_relay is not None: L.l.info("Got relay {} state={}".format(sensor_name, power_is_on)) new_relay = models.ZoneCustomRelay(gpio_pin_code=sensor_name, gpio_host_name=Constant.HOST_NAME) new_relay.relay_is_on = power_is_on current_relay.is_event_external = True models.ZoneCustomRelay().save_changed_fields(current_record=current_relay, new_record=new_relay, notify_transport_enabled=True, save_to_graph=True) else: L.l.error("ZoneCustomRelay with code {} does not exist in database".format(sensor_name)) elif 'COUNTER' in obj: # TelePeriod 60 counter = obj['COUNTER'] for i in [1, 2, 3, 4]: c = 'C{}'.format(i) if c in counter: cval = int(counter[c]) dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=cval, index=i) elif 'BMP280' in obj: # iot/sonoff/tele/sonoff-basic-3/SENSOR = # {"Time":"2018-10-28T08:12:26","BMP280":{"Temperature":24.6,"Pressure":971.0},"TempUnit":"C"} bmp = obj['BMP280'] temp = bmp['Temperature'] press = bmp['Pressure'] sensor_address = '{}_{}'.format(sensor_name, 'bmp280') current_zone_sensor = models.ZoneSensor.query.filter_by(sensor_address=sensor_address).first() if current_zone_sensor is not None: current_sensor = models.Sensor.query.filter_by(address=sensor_address).first() sensor = models.Sensor(address=sensor_address, sensor_name=current_zone_sensor.sensor_name) sensor.temperature = temp sensor.pressure = press sensor.save_changed_fields( current_record=current_sensor, notify_transport_enabled=True, save_to_graph=True) else: L.l.info('Undefined sensor found in {}, value={}'.format(sensor_address, bmp)) else: L.l.warning("Usefull payload missing from topic {} payload={}".format(msg.topic, msg.payload)) else: L.l.warning("Invalid sensor topic {}".format(msg.topic))
def save_to_db(dev): # global db_lock # db_lock.acquire() try: delta_time_counters = None address = dev['address'] record = models.Sensor(address=address) assert isinstance(record, models.Sensor) zone_sensor = models.ZoneSensor.query.filter_by( sensor_address=address).first() current_record = models.Sensor.query.filter_by(address=address).first() if zone_sensor: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = '{} {} {}'.format(address, dev['n_address'], dev['type']) record.type = dev['type'] record.updated_on = utils.get_base_location_now_date() if dev.has_key('counters_a'): record.counters_a = dev['counters_a'] if current_record: record.delta_counters_a = record.counters_a - current_record.counters_a # get accurate interval (e.g. to establish power consumption in watts) delta_time_counters = ( utils.get_base_location_now_date() - current_record.updated_on).total_seconds() else: # when running first time with db empty record.delta_counters_a = 0 # don't know prev. count, assume no consumption (ticks could be lost) delta_time_counters = P.sampling_period_seconds if dev.has_key('counters_b'): record.counters_b = dev['counters_b'] if current_record: record.delta_counters_b = record.counters_b - current_record.counters_b else: # fixme: don't know prev. count, assume no consumption (ticks could be lost) record.delta_counters_b = 0 if dev.has_key('temperature'): record.temperature = dev['temperature'] if dev.has_key('humidity'): record.humidity = dev['humidity'] if dev.has_key('iad'): record.iad = dev['iad'] if dev.has_key('vad'): record.vad = dev['vad'] if dev.has_key('vdd'): record.vdd = dev['vdd'] if dev.has_key('pio_a'): record.pio_a = dev['pio_a'] if dev.has_key('pio_b'): record.pio_b = dev['pio_b'] if dev.has_key('sensed_a'): record.sensed_a = dev['sensed_a'] if dev.has_key('sensed_b'): record.sensed_b = dev['sensed_b'] # force field changed detection for delta_counters to enable save in history # but allow one 0 record to be saved for nicer graphics if current_record is not None: if record.delta_counters_a != 0: current_record.delta_counters_a = 0 if record.delta_counters_b != 0: current_record.delta_counters_b = 0 record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=True, save_to_graph=True, debug=False) if record.delta_counters_a is not None or record.delta_counters_b is not None: dispatcher.send(Constant.SIGNAL_UTILITY, sensor_name=record.sensor_name, units_delta_a=record.delta_counters_a, units_delta_b=record.delta_counters_b, total_units_a=record.counters_a, total_units_b=record.counters_b, sampling_period_seconds=delta_time_counters) if record.vad is not None: dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=record.sensor_name, value=record.vad) except Exception as ex: L.l.error('Error saving sensor to DB, err {}'.format(ex), exc_info=True)
def set_main_heat_source(): heat_source_relay_list = models.ZoneHeatRelay.query.filter( models.ZoneHeatRelay.temp_sensor_name is not None).all() up_limit = P.temp_limit + P.threshold for heat_source_relay in heat_source_relay_list: # is there is a temp sensor defined, consider this source as possible alternate source if heat_source_relay.temp_sensor_name is not None: temp_rec = models.Sensor().query_filter_first( models.Sensor.sensor_name.in_( [heat_source_relay.temp_sensor_name])) # if alternate source is valid # fixok: add temp threshold to avoid quick on/offs if temp_rec is not None \ and ((temp_rec.temperature >= up_limit and not heat_source_relay.is_alternate_source_switch) or (temp_rec.temperature >= P.temp_limit and heat_source_relay.is_alternate_source_switch)): if heat_source_relay.is_alternate_source_switch: # stop main heat source heatrelay_main_source = models.ZoneHeatRelay.query.filter_by( is_main_heat_source=1).first() main_source_zone = models.Zone.query.filter_by( id=heatrelay_main_source.zone_id).first() __save_heat_state_db(zone=main_source_zone, heat_is_on=False) # turn switch valve on to alternate position switch_source_zone = models.Zone.query.filter_by( id=heat_source_relay.zone_id).first() __save_heat_state_db(zone=switch_source_zone, heat_is_on=True) else: # mark this source as active, to be started when there is heat need if heat_source_relay.is_alternate_heat_source is False: L.l.info( 'Alternate heat source is active with temp={}'. format(temp_rec.temperature)) heat_source_relay.is_alternate_heat_source = True commit() else: # if alternate source is no longer valid if heat_source_relay.is_alternate_source_switch: # stop alternate heat source #heatrelay_alt_source = models.ZoneHeatRelay.query.filter_by(is_alternate_heat_source=1).first() #if heatrelay_alt_source is not None: # alt_source_zone = models.Zone.query.filter_by(id=heatrelay_alt_source.zone_id).first() # __save_heat_state_db(zone=alt_source_zone, heat_is_on=False) # turn valve back to main position switch_source_zone = models.Zone.query.filter_by( id=heat_source_relay.zone_id).first() __save_heat_state_db(zone=switch_source_zone, heat_is_on=False) else: # mark this source as inactive, let main source to start if heat_source_relay.is_alternate_heat_source: # force alt source shutdown if was on alt_source_zone = models.Zone.query.filter_by( id=heat_source_relay.zone_id).first() __save_heat_state_db(zone=alt_source_zone, heat_is_on=False) # todo: sleep needed to allow for valve return if heat_source_relay.is_alternate_heat_source is True: L.l.info( 'Alternate heat source is now inactive, temp source is {}' .format(temp_rec)) heat_source_relay.is_alternate_heat_source = False commit()
def rule_sensor_temp_target(obj=models.Sensor(), field_changed_list=None): if not field_changed_list: field_changed_list = [] #temp = obj.temperature return 'rule temp ok'