def set_amp_power(power_state, relay_name, amp_zone_index): try: relay = models.ZoneCustomRelay.query.filter_by(relay_pin_name=relay_name).first() power_state = bool(power_state) if relay is not None: initial_relay_state = relay.relay_is_on # power on main relay for amp or on/off if there is no zone if power_state is True or amp_zone_index == 0: relay.relay_is_on = power_state commit() # dispatch as UI action otherwise change actions are not triggered dispatcher.send(signal=Constant.SIGNAL_UI_DB_POST, model=models.ZoneCustomRelay, row=relay) msg = "Set relay {} to state {} zone_index={}\n".format(relay_name, power_state, amp_zone_index) #L.l.debug(msg) else: msg = "Not changed relay state for {}\n".format(relay_name) else: msg = "Could not find relay name {}\n".format(relay_name) L.l.warning(msg) return msg # change amp zone power if amp_zone_index is None or amp_zone_index == 0: # only main relay change is needed return msg + "Power in {} set to {}\n".format(relay_name, relay.relay_is_on) else: # potentially amp settings change is required to switch amp zones if initial_relay_state is not True and power_state is True: # delay to wait for amp to fully start time.sleep(5) result_amp = amp_zone_power(power_state, amp_zone_index) return msg + result_amp except Exception as ex: L.l.error("Error set_amp_power {}".format(ex)) return "Error set_amp_power {}".format(ex)
def setup_in_ports(gpio_pin_list): # Log.logger.info('Socket timeout={}'.format(socket.getdefaulttimeout())) # socket.setdefaulttimeout(None) global __callback, __pi L.l.info('Configuring {} gpio input ports'.format(len(gpio_pin_list))) if __pi: if socket.getdefaulttimeout() is not None: L.l.critical('PiGpio callbacks cannot be started as socket timeout is not None') else: for gpio_pin in gpio_pin_list: if gpio_pin.pin_type == Constant.GPIO_PIN_TYPE_PI_STDGPIO: try: L.l.info('Set pincode={} type={} index={} as input'.format(gpio_pin.pin_code, gpio_pin.pin_type, gpio_pin.pin_index_bcm)) __pi.set_mode(int(gpio_pin.pin_index_bcm), pigpio.INPUT) # https://learn.sparkfun.com/tutorials/pull-up-resistors __pi.set_pull_up_down(int(gpio_pin.pin_index_bcm), pigpio.PUD_UP) __callback.append(__pi.callback(user_gpio=int(gpio_pin.pin_index_bcm), edge=pigpio.EITHER_EDGE, func=input_event)) gpio_pin_record = models.GpioPin().query_filter_first( models.GpioPin.pin_code.in_([gpio_pin.pin_code]), models.GpioPin.host_name.in_([Constant.HOST_NAME])) gpio_pin_record.pin_direction = Constant.GPIO_PIN_DIRECTION_IN commit() except Exception, ex1: L.l.critical('Unable to setup pigpio_gpio pin, er={}'.format(ex1)) else: L.l.info('Skipping PiGpio setup for pin {} with type {}'.format(gpio_pin.pin_code, gpio_pin.pin_type)) L.l.info('Exit gpio callback thread loop')
def generic_db_update(model_name, filter_name, filter_value, field_name, field_value): try: L.l.info("Execute API generic_db_update model={} filter={} filtervalue={} field={} fieldvalue={}" .format(model_name, filter_name, filter_value, field_name, field_value)) table = utils.class_for_name('main.admin.models', model_name) # http://stackoverflow.com/questions/19506105/flask-sqlalchemy-query-with-keyword-as-variable kwargs = {filter_name: filter_value} # avoid getting "This session is in 'committed' state; no further SQL can be emitted within this transaction" db.session.remove() # db.session = db.create_scoped_session() record = table.query.filter_by(**kwargs).first() if record: if hasattr(record, field_name): setattr(record, field_name, field_value) db.session.add(record) commit() # dispatch as UI action otherwise change actions are not triggered dispatcher.send(signal=Constant.SIGNAL_UI_DB_POST, model=table, row=record) return '%s: %s' % (Constant.SCRIPT_RESPONSE_OK, record) else: msg = 'Field {} not found in record {}'.format(field_name, record) L.l.warning(msg) return '%s: %s' % (Constant.SCRIPT_RESPONSE_NOTOK, msg) else: msg = 'No records returned for filter_name={} and filter_value={}'.format(filter_name, filter_value) L.l.warning(msg) return '%s: %s' % (Constant.SCRIPT_RESPONSE_NOTOK, msg) except Exception as ex: msg = 'Exception on /apiv1/db_update: {}'.format(ex) L.l.error(msg, exc_info=1) return '%s: %s' % (Constant.SCRIPT_RESPONSE_NOTOK, msg) finally: # db.session.remove() pass
def record_update(obj_dict=None): if not obj_dict: obj_dict = {} try: source_host_name = utils.get_object_field_value( obj_dict, Constant.JSON_PUBLISH_SOURCE_HOST) zone_id = utils.get_object_field_value( obj_dict, utils.get_model_field_name(models.ZoneHeatRelay.zone_id)) pin_name = utils.get_object_field_value( obj_dict, utils.get_model_field_name(models.ZoneHeatRelay.heat_pin_name)) is_on = utils.get_object_field_value( obj_dict, utils.get_model_field_name(models.ZoneHeatRelay.heat_is_on)) # fixme: remove hard reference to object field sent_on = utils.get_object_field_value(obj_dict, "event_sent_datetime") L.l.debug( 'Received heat relay update from {} zoneid={} pin={} is_on={} sent={}' .format(source_host_name, zone_id, pin_name, is_on, sent_on)) zone_heat_relay = models.ZoneHeatRelay.query.filter_by( zone_id=zone_id).first() if zone_heat_relay: gpio_host_name = zone_heat_relay.gpio_host_name cmd_heat_is_on = utils.get_object_field_value( obj_dict, utils.get_model_field_name(models.ZoneHeatRelay.heat_is_on)) L.l.debug( 'Local heat state zone_id {} must be changed to {} on pin {}'. format(zone_id, cmd_heat_is_on, zone_heat_relay.gpio_pin_code)) if cmd_heat_is_on: pin_value = 1 else: pin_value = 0 # set pin only on pins owned by this host if zone_heat_relay and gpio_host_name == Constant.HOST_NAME: L.l.info("Setting heat pin {} to {}".format( zone_heat_relay.gpio_pin_code, pin_value)) pin_state = gpio.relay_update( gpio_pin_code=zone_heat_relay.gpio_pin_code, pin_value=pin_value) else: pin_state = pin_value if pin_state == pin_value: pin_state = (pin_state == 1) zone_heat_relay.heat_is_on = pin_state zone_heat_relay.notify_transport_enabled = False commit() else: L.l.warning( 'Heat state zone_id {} unexpected val={} after setval={}'. format(zone_id, pin_state, pin_value)) else: L.l.warning( 'No heat relay defined for zone {}, db data issue?'.format( zone_id)) except Exception as ex: L.l.warning('Error updating heat relay state, err {}'.format(ex))
def get_pin_bcm(bcm_id=''): '''BCM pin id format. Return value is 0 or 1.''' if not __is_pin_setup(bcm_id): __set_pin_dir_in(bcm_id) if __is_pin_setup(bcm_id): pin_value = __read_line(bcm_id) gpio_pin = models.GpioPin.query.filter_by(pin_index_bcm=bcm_id, host_name=Constant.HOST_NAME).first() if gpio_pin: gpio_pin.pin_value = pin_value commit() return pin_value else: L.l.critical('Unable to get pin bcm {}'.format(bcm_id))
def __set_pin_dir_in(bcm_id=''): try: # file = open('/sys/class/gpio/gpio{}/direction'.format(bcm_id), 'a') # print >> file, 'in' # file.close() __setup_pin(bcm_id) if __write_to_file_as_root(file='/sys/class/gpio/gpio{}/direction'.format(bcm_id), value='in'): L.l.info('Set pin {} direction IN is OK'.format(bcm_id)) gpio_pin = __get_gpio_db_pin(bcm_id) if gpio_pin: gpio_pin.pin_direction = 'in' commit() return True except Exception, ex: L.l.warning('Unexpected exception on pin {} direction IN set, err {}'.format(bcm_id, ex)) return False
def __is_pin_setup(bcm_id=''): try: file = open('/sys/class/gpio/gpio{}/value'.format(bcm_id), 'r') file.close() gpio_pin = models.GpioPin.query.filter_by(pin_index_bcm=bcm_id, host_name=Constant.HOST_NAME).first() if gpio_pin and not gpio_pin.is_active: L.l.warning( 'Gpio pin={} is used not via me, conflict with ext. apps or unclean stop?'.format(bcm_id)) gpio_pin.is_active = True commit() return True except IOError: return False except Exception, ex: L.l.warning('Unexpected exception on pin setup check, err {}'.format(ex)) db.session.rollback() return False
def __save_heat_state_db(zone, heat_is_on): assert isinstance(zone, models.Zone) zone_heat_relay = models.ZoneHeatRelay.query.filter_by( zone_id=zone.id).first() if zone_heat_relay is not None: zone_heat_relay.heat_is_on = heat_is_on zone_heat_relay.updated_on = utils.get_base_location_now_date() L.l.debug( 'Heat state changed to is-on={} via pin={} in zone={}'.format( heat_is_on, zone_heat_relay.heat_pin_name, zone.name)) zone_heat_relay.notify_transport_enabled = True zone_heat_relay.save_to_graph = True zone_heat_relay.save_to_history = True # save latest heat state for caching purposes zone.heat_is_on = heat_is_on zone.last_heat_status_update = utils.get_base_location_now_date() commit() else: L.l.warning('No heat relay found in zone {}'.format(zone.name))
def handle_event_alarm(gpio_pin_code='', direction='', pin_value='', pin_connected=None): zonealarm = models.ZoneAlarm.query.filter_by( gpio_pin_code=gpio_pin_code, gpio_host_name=Constant.HOST_NAME).first() if zonealarm is not None: zonearea = models.ZoneArea().query_filter_first( models.ZoneArea.zone_id == zonealarm.zone_id) if zonearea is not None: area = models.Area().query_filter_first( models.Area.id == zonearea.area_id) if area is not None: zonealarm.start_alarm = area.is_armed zone = models.Zone.query.filter_by( id=zonealarm.zone_id).first() if zone is not None: dispatcher.send(signal=Constant.SIGNAL_ALARM, zone_name=zone.name, alarm_pin_name=zonealarm.alarm_pin_name, pin_connected=pin_connected) # L.l.info('Got alarm {} zone={} pin={} pin_conn={} pin_value={} gpio={}'.format( # zone.name, zonealarm.zone_id, zonealarm.alarm_pin_name, # pin_connected, pin_value, gpio_pin_code)) else: L.l.error( "Could not find zone for gpio pin {}, trigger actions could be missed" .format(gpio_pin_code)) zonealarm.alarm_pin_triggered = not pin_connected zonealarm.updated_on = utils.get_base_location_now_date() zonealarm.notify_transport_enabled = True commit() else: L.l.warning('Zone {} is mapped to missing area' % zonealarm.zone_id) else: L.l.warning('Zone %s not mapped to an area' % zonealarm.zone_id) else: L.l.info('Mising zone alarm for gpio code {}'.format(gpio_pin_code))
def __update_zone_heat(zone, heat_schedule, sensor): heat_is_on = False main_source_needed = True try: minute = utils.get_base_location_now_date().minute hour = utils.get_base_location_now_date().hour weekday = datetime.datetime.today().weekday() # todo: insert here auto heat change based on presence status if weekday <= 4: # Monday=0 schedule_pattern = models.SchedulePattern.query.filter_by( id=heat_schedule.pattern_week_id).first() else: schedule_pattern = models.SchedulePattern.query.filter_by( id=heat_schedule.pattern_weekend_id).first() if schedule_pattern: main_source_needed = schedule_pattern.main_source_needed if main_source_needed is False: L.l.info( "Main heat source is not needed for zone {}".format(zone)) force_off = False # set heat to off if condition is met (i.e. do not try to heat water if heat source is cold) if schedule_pattern.activate_on_condition: relay_name = schedule_pattern.activate_condition_relay zone_heat_relay = models.ZoneHeatRelay.query.filter_by( heat_pin_name=relay_name).first() if zone_heat_relay is not None: force_off = zone_heat_relay.heat_is_on is False if force_off: L.l.info( 'Deactivating heat in zone {} due to relay {}'. format(zone, zone_heat_relay)) else: L.l.error('Could not find the heat relay for zone heat {}'. format(zone)) # strip formatting characters that are not used to represent target temperature pattern = str(schedule_pattern.pattern).replace('-', '').replace( ' ', '') # check pattern validity if len(pattern) == 24: temperature_code = pattern[hour] temperature_target = models.TemperatureTarget.query.filter_by( code=temperature_code).first() # check if we need forced heat on, if for this hour temp has a upper target than min force_on = False if schedule_pattern.keep_warm: if len(schedule_pattern.keep_warm_pattern) == 20: interval = int(minute / 5) delta_warm = sensor.temperature - temperature_target.target if delta_warm <= P.MAX_DELTA_TEMP_KEEP_WARM: force_on = ( (schedule_pattern.keep_warm_pattern[interval] == "1") and temperature_code is not P.TEMP_NO_HEAT) else: L.l.info( "Temperature is too high with delta {}, ignoring keep warm" .format(delta_warm)) else: L.l.critical( "Missing keep warm pattern for zone {}".format( zone.name)) if temperature_target: if zone.active_heat_schedule_pattern_id != schedule_pattern.id: L.l.debug( 'Pattern in zone {} changed to {}, target={}'. format(zone.name, schedule_pattern.name, temperature_target.target)) zone.active_heat_schedule_pattern_id = schedule_pattern.id zone.heat_target_temperature = temperature_target.target commit() if sensor.temperature is not None: heat_is_on = __decide_action(zone, sensor.temperature, temperature_target.target, force_on=force_on, force_off=force_off) #else: # heat_is_on = zone.heat_is_on else: L.l.critical('Unknown temperature pattern code {}'.format( temperature_code)) else: L.l.warning( 'Incorrect temp pattern [{}] in zone {}, length is not 24'. format(pattern, zone.name)) except Exception as ex: L.l.error('Error updatezoneheat, err={}'.format(ex, exc_info=True)) #Log.logger.info("Temp in {} has target={} and current={}, heat should be={}".format(zone.name, # zone.heat_target_temperature, # sensor.temperature, heat_is_on)) return heat_is_on, main_source_needed
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 update_master_state(): master_overall_selected = False try: alive_date_time = utils.get_base_location_now_date( ) - datetime.timedelta(minutes=1) node_list = models.Node.query.order_by(models.Node.priority).all() for node in node_list: #if recently alive, in order of id prio if node.updated_on >= alive_date_time and ( not master_overall_selected): if node.is_master_overall: L.l.debug( 'Node {} is already master, all good we have a master'. format(node.name)) master_overall_selected = True else: L.l.info('Node {} will become a master'.format(node.name)) if node.name == Constant.HOST_NAME: node.is_master_overall = True node.notify_enabled_ = True commit() master_overall_selected = True else: if master_overall_selected and node.is_master_overall: L.l.debug( 'Node {} should lose master status, if alive'.format( node.name)) if node.name == Constant.HOST_NAME: node.is_master_overall = False node.notify_enabled_ = True commit() # applying node master status locally, if changed in db if node.name == Constant.HOST_NAME: variable.NODE_THIS_IS_MASTER_LOGGING = node.is_master_logging if variable.NODE_THIS_IS_MASTER_OVERALL != node.is_master_overall: if not node.is_master_overall: # disabling local mastership immediately variable.NODE_THIS_IS_MASTER_OVERALL = node.is_master_overall L.l.info( 'Immediate change in node mastership, local node is master={}' .format(variable.NODE_THIS_IS_MASTER_OVERALL)) else: global since_when_i_should_be_master # check seconds lapsed since cluster agreed I must be or lose master seconds_elapsed = ( utils.get_base_location_now_date() - since_when_i_should_be_master).total_seconds() if check_if_no_masters_overall( ) or seconds_elapsed > 10: variable.NODE_THIS_IS_MASTER_OVERALL = node.is_master_overall L.l.info( 'Change in node mastership, local node is master={}' .format(variable.NODE_THIS_IS_MASTER_OVERALL)) since_when_i_should_be_master = datetime.datetime.max else: # L.l.info('Waiting to set master status, sec. lapsed={}'.format(seconds_elapsed)) pass if not variable.NODE_THIS_IS_MASTER_OVERALL: # record date when cluster agreed I must be master if since_when_i_should_be_master == datetime.datetime.max: since_when_i_should_be_master = utils.get_base_location_now_date( ) except Exception as ex: L.l.warning('Error try_become_master, err {}'.format(ex))