def setup_timers(self): logger.info("setting up cron") callbacks = {} for prop in self.properties: interval = self.properties[prop]['interval'] if interval not in callbacks: callbacks[interval] = CallBack(self) callbacks[interval].add_property(prop) for cb in callbacks: repeating_timer = Repeating_Timer(cb) repeating_timer.add_callback(callbacks[cb])
def start( self ): # called after the device has been built with nodes and properties logger.debug('Device startup') self.start_time = time.time() global repeating_timer if repeating_timer == None: repeating_timer = Repeating_Timer( self.homie_settings['update_interval']) repeating_timer.add_callback(self.publish_statistics) if self.mqtt_client.mqtt_connected: #run start up tasks if mqtt is ready, else wait for on_connect message from mqtt client self.mqtt_on_connection(True)
def __init__( self, device_id=None, name=None, homie_settings=None, mqtt_settings=None, device_comfoairq=None, connect_at_start=True, ): assert device_id assert name assert device_comfoairq assert mqtt_settings super().__init__ (device_id, name, homie_settings, mqtt_settings) self.device_comfoairq=device_comfoairq # Gateway Controls node = Node_Base(self,'controls','ComfoAirQ Gateway Controls','controls') self.add_node (node) node.add_property(Property_Switch(node,id='stayconnected',name = 'Stay connected to Comfoconnect' ,settable = True, set_value=self.set_stay_connected)) node.add_property(Property_Switch(node,id='reload',name = 'Publish Homie Devices' ,settable = True, set_value=self.set_reload)) node = Node_Base(self,'sensors','ComfoAirQ Gateway Sensors','sensors') self.add_node (node) node.add_property (Property_Enum (node,id='state',name = 'Connection state',settable = False,data_format=','.join(DEVICE_STATES))) if self.device_comfoairq is not None: self.device_comfoairq.comfoairq.add_on_state_change_callback(self.publish_connection_status) global repeating_timer if repeating_timer == None: repeating_timer = Repeating_Timer( self.homie_settings["update_interval"] ) repeating_timer.add_callback(self.publish_connection_status) self.start() if connect_at_start: self.set_stay_connected("ON") self.publish_connection_status()
def start(self): # called after the device has been built with nodes and properties logger.info("Device Start {}".format(self.name)) self.start_time = time.time() if "stats" in self.extensions: global repeating_timer if repeating_timer == None: repeating_timer = Repeating_Timer( self.homie_settings["update_interval"] ) repeating_timer.add_callback(self.publish_uptime) if ( self.mqtt_client.mqtt_connected ): # run start up tasks if mqtt is ready, else wait for on_connect message from mqtt client self.mqtt_on_connection(True)
def start_publishing_loop(self): # assign timer to publish info self._timer_upd_interval = Repeating_Timer(self._upd_interval) self._timer_upd_interval.add_callback(self._publish_data)
class Device_BME680(Device_Base): _MAX_RESISTANCE = 350000 _MIN_RESISTANCE = 10000 _HUMIDITY_WEIGHT = 0.25 _upd_interval = 60 _ideal_rel_humidity = 40. _sensor_settings = { "temperature_bias": 0., "humidity_bias": 0., "pressure_bias": 0. } def __init__(self, device_id=None, name=None, sensor_settings={}, homie_settings=None, mqtt_settings=None): super().__init__(device_id, name, homie_settings, mqtt_settings) # assign sensor try: sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY) except IOError: sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY) #sensor.set_humidity_oversample(bme680.OS_2X) sensor.set_humidity_oversample(bme680.OS_8X) #sensor.set_pressure_oversample(bme680.OS_4X) #sensor.set_temperature_oversample(bme680.OS_8X) #sensor.set_filter(bme680.FILTER_SIZE_3) #sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) sensor.set_gas_heater_temperature(320) sensor.set_gas_heater_duration(150) sensor.select_gas_heater_profile(0) self._sensor = sensor self._sensor_settings.update(sensor_settings) # assign Homie features to update/request information on demand node_worker = Node_Base(self, id="worker-node", name="Data worker node", type_="status") self.add_node(node_worker) upd_interval = Property_Integer(node=node_worker, id="update-interval", name="Statistics update interval", unit="s", settable=True, set_value=self._set_upd_interval, value=self._upd_interval, data_format="5:3600") node_worker.add_property(upd_interval) ideal_rel_humidity = Property_Integer(node=node_worker, id="update-ideal-rel-humidity", name="Update ideal relative humidity for AQI calculations", unit="%", settable=True, set_value=self._set_ideal_rel_humidity, value=self._ideal_rel_humidity, data_format="0:100") node_worker.add_property(ideal_rel_humidity) # assign Homie features to base sensor node_sensor = Node_Base(self, id="multi-sensor", name="Multiple sensor", type_="status") self.add_node(node_sensor) pressure_prop = Property_Pressure(node=node_sensor, unit="hPa") node_sensor.add_property(pressure_prop) pressure_bias_prop = Property_Pressure(node=node_sensor, id="pressure-bias", unit="hPa", settable=False, value=self._sensor_settings["pressure_bias"]) node_sensor.add_property(pressure_bias_prop) self._pressure = pressure_prop humidity_prop = Property_Humidity(node=node_sensor) node_sensor.add_property(humidity_prop) humidity_bias_prop = Property_Humidity(node=node_sensor, id="humidity-bias", settable=False, value=self._sensor_settings["humidity_bias"]) node_sensor.add_property(humidity_bias_prop) self._humidity = humidity_prop temperature_prop = Property_Temperature(node=node_sensor, unit="°C") node_sensor.add_property(temperature_prop) temperature_bias_prop = Property_Temperature(node=node_sensor, id="temperature-bias", unit="°C", settable=False, value=self._sensor_settings["temperature_bias"]) node_sensor.add_property(temperature_bias_prop) self._temperature = temperature_prop gas_res_prop = Property_Float(node=node_sensor, id="gas-resistance", name="Gas resistance", settable=False, unit="Ohm") node_sensor.add_property(gas_res_prop) self._gas_resistance = gas_res_prop aqi_prop = Property_Integer(node=node_sensor, id="aqi", name="Air quality index", settable=False) node_sensor.add_property(aqi_prop) self._aqi = aqi_prop # start self.start() def _get_core_data(self): self._sensor.get_sensor_data() if self._mqtt_connected: self._pressure.value = self._sensor.data.pressure + self._sensor_settings["pressure_bias"] self._temperature.value = self._sensor.data.temperature + self._sensor_settings["temperature_bias"] self._humidity.value = self._sensor.data.humidity + self._sensor_settings["humidity_bias"] if self._sensor.data.heat_stable: adj_resistance = self._adj_gas_resistance(self._sensor.data.gas_resistance, self._sensor.data.humidity) self._gas_resistance.value = adj_resistance self._aqi.value = self._calculate_aqi(adj_resistance, self._sensor.data.humidity) else: pass def _adj_gas_resistance(self, gas_resistance, humidity): # based on observations, gas resistance is dependent on humidity more than temperature # so i adjust for it and changed the parameter # https://forums.pimoroni.com/t/bme680-observed-gas-ohms-readings/6608/16 return math.exp(math.log(gas_resistance) + 0.023 * humidity) def _calculate_aqi(self, gas_resistance, humidity): if humidity < self._ideal_rel_humidity: #if humidity == ideal_rel_humidity, then result is 0. #if humidity approaches 0, result is HUMIDITY WEIGHT hum_score = self._HUMIDITY_WEIGHT * (self._ideal_rel_humidity - humidity) / self._ideal_rel_humidity else: #if humidity == ideal_rel_humidity, then result is 0. #if humidity approaches 100, result is HUMIDITY_WEIGHT hum_score = self._HUMIDITY_WEIGHT * (humidity - self._ideal_rel_humidity) / (100. - self._ideal_rel_humidity) if gas_resistance > self._MAX_RESISTANCE: gas_resistance = self._MAX_RESISTANCE - 1.0 #to avoid numerical errors below in ratios elif gas_resistance < self._MIN_RESISTANCE: gas_resistance = self._MIN_RESISTANCE + 1.0 gas_score = (1. - self._HUMIDITY_WEIGHT) * (1. - (gas_resistance - self._MIN_RESISTANCE)/(self._MAX_RESISTANCE - self._MIN_RESISTANCE)) return int(500 * (gas_score + hum_score)) def _publish_data(self): if self.mqtt_client.mqtt_connected: self._get_core_data() @property def ideal_rel_humidity(self): return self._ideal_rel_humidity #needed for set_value routine in node Property def _set_ideal_rel_humidity(self): self.ideal_rel_humidity = value @ideal_rel_humidity.setter def ideal_rel_humidity(self,value): self._ideal_rel_humidity = value @property def upd_interval(self): return self._upd_interval #needed for set_value routine in node Property def _set_upd_interval(self, value): self.upd_interval = value @upd_interval.setter def upd_interval(self, value): self._upd_interval = value self._timer_upd_interval.interval = value def start_publishing_loop(self): # assign timer to publish info self._timer_upd_interval = Repeating_Timer(self._upd_interval) self._timer_upd_interval.add_callback(self._publish_data) def stop_publishing_loop(self): try: if self._timer_upd_interval.timer.is_alive(): self._timer_upd_interval.stop() except AttributeError: pass
def __init__( self, device_id=None, name=None, homie_settings=None, mqtt_settings=None, sensors_definition = comfoairq_sensors, comfoconnect_settings=None, ): assert comfoconnect_settings assert mqtt_settings super().__init__ (device_id, name, homie_settings, mqtt_settings) self.state = 'init' global repeating_timer if repeating_timer == None: repeating_timer = Repeating_Timer( self.homie_settings["update_interval"] ) repeating_timer.add_callback(self.publish_connection_status) self.sensors = sensors_definition self.comfoconnect_settings=comfoconnect_settings self.min_low_flow = self.comfoconnect_settings['COMFOCONNECT_MIN_LOW_FLOW'] self.max_high_flow = self.comfoconnect_settings['COMFOCONNECT_MAX_HIGH_FLOW'] self.comfoairq = ComfoAirQ(comfoconnect_settings=self.comfoconnect_settings) self.comfoairq.callback_sensor = self.callback_sensor self.comfoairq.add_on_state_change_callback(self.publish_connection_status) # Sensors node = Node_Base(self,'sensors','Sensors','sensors') self.add_node (node) for comfoairq_sensor in self.sensors: self.comfoairq.register_sensor(comfoairq_sensor) for homie_sensor in self.sensors[comfoairq_sensor]: sensor_id ,sensor_name ,sensor_type, transformation_function, function_args = homie_sensor if sensor_type == 'temperature': node.add_property (Property_Temperature (node,id=sensor_id,name = sensor_name, unit='°C')) elif sensor_type == 'fan_speed': node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='rpm')) elif sensor_type == 'fan_flow': node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='m³/h')) elif sensor_type == 'percentage': node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='%')) elif sensor_type == 'mode_end_date': node.add_property (Property_DateTime (node,id=sensor_id,name = sensor_name,settable = False,data_format='%Y-%m-%d %H:%M:%S.%f')) elif sensor_type == 'mode_timer': node.add_property (Property_Integer (node,id=sensor_id,name = sensor_name,settable = False,unit='s')) elif sensor_type == 'enum': node.add_property (Property_Enum (node,id=sensor_id,name = sensor_name,settable = False,data_format=','.join(function_args.values()))) elif sensor_type == 'power_current': node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='W')) elif sensor_type == 'energy': node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='kWh')) elif sensor_type == 'integer': node.add_property (Property_Integer(node,id=sensor_id,name = sensor_name,settable = False)) # Sensors managed same way as Controls node.add_property(Property_Enum (node,id='current-mode',name ='Current Mode',settable = False,data_format=','.join(CURRENT_OPERATING_MODE_SENSOR_VALUES.values()))) self.add_controls_callback(SENSOR_OPERATING_MODE_BIS,self.update_current_mode) self.add_controls_callback(SENSOR_OPERATING_MODE,self.update_current_mode) # Controls node = Node_Base(self,'controls','Controls','controls') self.add_node (node) # FAN SPEED MODE node.add_property(Property_Enum (node,id='fan-mode',name='Fan Mode',data_format=','.join(FAN_MODES.keys()),set_value = lambda value: self.set_fan_mode(value))) self.add_controls_callback(SENSOR_FAN_SPEED_MODE,self.update_fan_mode) # BYPASS MODE (for 1 hour) node.add_property(Property_Enum (node,id='bypass-mode',name='Bypass Mode',data_format=','.join(BYPASS_MODES.keys()),set_value = lambda value: self.set_bypass_mode(value))) self.add_controls_callback(SENSOR_BYPASS_MODE,self.update_bypass_mode) # BYPASS ON node.add_property(Property_Integer (node,id='bypass-on', name='Bypass On',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_bypass_on(value))) # BYPASS OFF node.add_property(Property_Integer (node,id='bypass-off', name='Bypass Off',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_bypass_off(value))) self.add_controls_callback(SENSOR_BYPASS_MODE,self.update_bypass) self.add_controls_callback(SENSOR_BYPASS_TIMER,self.update_bypass) # TEMPERATURE PROFILE node.add_property(Property_Enum (node,id='temperature-profile',name='Temperature Profile',data_format=','.join(TEMPERATURE_PROFILES.keys()),set_value = lambda value: self.set_temperature_profile(value))) self.add_controls_callback(SENSOR_TEMPERATURE_PROFILE,self.update_temperature_profile) # OPERATING MODE node.add_property(Property_Enum (node,id='operating-mode',name='Operating Mode',data_format=','.join(OPERATING_MODES.keys()),set_value = lambda value: self.set_operating_mode(value))) self.add_controls_callback(SENSOR_OPERATING_MODE,self.update_operating_mode) # Manual MODE node.add_property(Property_Switch (node,id='manual-mode',name='Manual Mode',set_value = lambda value: self.set_manual_mode(value))) self.add_controls_callback(SENSOR_OPERATING_MODE,self.update_manual_mode) # VENT MODE node.add_property(Property_Enum (node,id='vent-mode',name='Ventilation Mode',data_format=','.join(VENT_MODES.keys()),set_value = lambda value: self.set_vent_mode(value))) self.add_controls_callback(SENSOR_TEMPORARY_STOP_EXHAUST_FAN_STATE,self.update_vent_mode) self.add_controls_callback(SENSOR_TEMPORARY_STOP_SUPPLY_FAN_STATE,self.update_vent_mode) # SUPPLY FAN OFF node.add_property(Property_Integer (node,id='supply-fan-off', name='Supply Fan Off',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_supply_fan_off(value))) self.add_controls_callback(SENSOR_SUPPLY_FAN_TIMER,self.update_supply_fan_off) # EXHAUST FAN OFF node.add_property(Property_Integer (node,id='exhaust-fan-off', name='Exhaust Fan Off',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_exhaust_fan_off(value))) self.add_controls_callback(SENSOR_EXHAUST_FAN_TIMER,self.update_exhaust_fan_off) # BOOST MODE node.add_property(Property_Integer (node,id='boost-mode',name='Activate Scheduled Boost Mode',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_boost_mode(value))) self.add_controls_callback(SENSOR_OPERATING_MODE_BIS,self.update_boost_mode) self.add_controls_callback(SENSOR_FAN_NEXT_CHANGE,self.update_boost_mode) # AWAY MODE node.add_property(Property_Integer (node,id='away-mode',name='Activate Scheduled Away Mode',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_away_mode(value))) self.add_controls_callback(SENSOR_OPERATING_MODE_BIS,self.update_away_mode) self.add_controls_callback(SENSOR_FAN_NEXT_CHANGE,self.update_away_mode) # Heating Season detection RMOT node.add_property(Property_Temperature (node,id='heating-rmot',name='Heating Limit RMOT',data_format='0:20',unit='°C',settable = True, set_value = lambda value: self.set_heating_rmot(value))) self.comfoairq.add_on_state_change_callback(self.update_heating_rmot) repeating_timer.add_callback(self.update_heating_rmot) # Cooling Season detection RMOT node.add_property(Property_Temperature (node,id='cooling-rmot',name='Cooling Limit RMOT',data_format='20:40',unit='°C',settable = True, set_value = lambda value: self.set_cooling_rmot(value))) self.comfoairq.add_on_state_change_callback(self.update_cooling_rmot) repeating_timer.add_callback(self.update_cooling_rmot) #Temperature passive node.add_property(Property_Enum (node,id='temperature-passive',name='Temperature Passive',data_format=','.join(SENSOR_VENTILATION),set_value = lambda value: self.set_temperature_passive(value))) self.comfoairq.add_on_state_change_callback(self.update_temperature_passive) repeating_timer.add_callback(self.update_temperature_passive) #Humidity comfort node.add_property(Property_Enum (node,id='humidity-comfort',name='Humidity Comfort',data_format=','.join(SENSOR_VENTILATION),set_value = lambda value: self.set_humidity_comfort(value))) self.comfoairq.add_on_state_change_callback(self.update_humidity_comfort) repeating_timer.add_callback(self.update_humidity_comfort) #Humidity protection node.add_property(Property_Enum (node,id='humidity-protection',name='Humidity Protection',data_format=','.join(SENSOR_VENTILATION),set_value = lambda value: self.set_humidity_protection(value))) self.comfoairq.add_on_state_change_callback(self.update_humidity_protection) repeating_timer.add_callback(self.update_humidity_protection) #Flow unbalance node.add_property(Property_Float (node,id='unbalance',name='Flow Unbalance',data_format='-15:15',set_value = lambda value: self.set_unbalance(value))) self.comfoairq.add_on_state_change_callback(self.update_unbalance) repeating_timer.add_callback(self.update_unbalance) #Flow Away node.add_property(Property_Integer (node,id='flow-away',name='Flow Away Speed',data_format='0:' + str(self.max_high_flow),set_value = lambda value: self.set_flow_away(value))) self.comfoairq.add_on_state_change_callback(self.update_flow_away) repeating_timer.add_callback(self.update_flow_away) #Flow Low node.add_property(Property_Integer (node,id='flow-low',name='Flow Low Speed',data_format= str(self.min_low_flow) + ':' + str(self.max_high_flow),set_value = lambda value: self.set_flow_low(value))) self.comfoairq.add_on_state_change_callback(self.update_flow_low) repeating_timer.add_callback(self.update_flow_low) #Flow Medium node.add_property(Property_Integer (node,id='flow-medium',name='Flow Medium Speed',data_format=str(self.min_low_flow) + ':' + str(self.max_high_flow),set_value = lambda value: self.set_flow_medium(value))) self.comfoairq.add_on_state_change_callback(self.update_flow_medium) repeating_timer.add_callback(self.update_flow_medium) #Flow High node.add_property(Property_Integer (node,id='flow-high',name='Flow High Speed',data_format=str(self.min_low_flow) + ':' + str(self.max_high_flow),set_value = lambda value: self.set_flow_high(value))) self.comfoairq.add_on_state_change_callback(self.update_flow_high) repeating_timer.add_callback(self.update_flow_high) self.start() self.publish_connection_status()