async def adjust_time(self, once=False): while True: self.get_time(set_rtc=True) if once: break await uasyncio.sleep(600) manage_memory()
def __init__(self, device, conf): RelaySwitchController.__init__(self, device, conf) self._power_monitor = None if conf.get('power_monitor'): manage_memory() from power_monitor import PowerMonitor try: self._power_monitor = PowerMonitor(conf['power_monitor'], device.i2c) except Exception as exc: LOG.exc(exc, 'Power monitor init error') self._reverse = Pin(conf['reverse'], Pin.INOUT) self.reverse = False self._reverse_threshold = device.settings.get('reverse_threshold') self._reverse_duration = device.settings.get('reverse_duration') self._expired_limit = device.settings.get('expired_limit') self._reverse_delay = 2 self._delay = 0 self.flag_pins = None if conf.get('buttons'): self._buttons = [] for idx, pin in enumerate(conf['buttons']): reverse = bool(idx) self._buttons.append( Pin(pin, Pin.IN, Pin.PULL_UP, handler=self.on_button_reverse if reverse else self.on_button, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)) if conf.get('flag_pins'): self.flag_pins = [ Pin(pin, Pin.IN, Pin.PULL_UP) for pin in conf['flag_pins'] ]
async def post_sensor_data(self, once=False): while True: if not once: await uasyncio.sleep(58) try: data = {'data': []} tstamp = self.post_tstamp() for ctrl in self.modules.values(): if ctrl and hasattr(ctrl, 'data_log'): data['data'] += [{'sensor_id': entry[0], 'tstamp': entry[1], 'value': entry[2]}\ for entry in ctrl.data_log] elif ctrl and hasattr(ctrl, 'data'): data['data'] += [{'sensor_id': _id, 'tstamp': tstamp, 'value': value}\ for _id, value in ctrl.data.items()] if data['data']: rsp = await self.srv_post('sensors_data', data, retry=once) if once and not rsp: machine.reset() if rsp: for ctrl in self.modules.values(): if ctrl and hasattr(ctrl, 'data_log'): ctrl.data_log = [] data = {'data': []} tstamp = self.post_tstamp() for ctrl in self.modules.values(): if ctrl and hasattr(ctrl, 'switches'): data['data'] += [{'device_type_switch_id': switch['id'], 'tstamp': tstamp, 'state': switch['pin'].value() == 1}\ for switch in ctrl.switches.values() if switch['enabled']] if data['data']: await self.srv_post('switches_state', data, retry=once) except Exception as exc: LOG.exc(exc, 'Server sensors data post error') manage_memory() if once: break
def on(self, value=True, manual=False): if self.state != value: if self._pulse_length: self._on = value else: self.pin.value(value) self.log_relay_switch('start' if value else 'stop', 'manual' if manual else 'timer') manage_memory()
async def check_timers(self): off = None while True: time = time_tuple_to_seconds(machine.RTC().now()) if time == 0: self.init_timers() for timer in self.timers: if timer.time_on <= time < timer.time_on + timer.duration: start = utime.time() now = start prev_time = start expired = time - timer.time_on retries = 0 self.on(source=timer) def continue_flag(): nonlocal retries, expired if retries >= 3: return False if self._expired_limit and expired > self._expired_limit: return False if timer.duration > 0 and expired > timer.duration: return False if self.flag_pins: flag_pin = self.flag_pins[1 if self.reverse else 0] if flag_pin.value(): self.device.append_log_entries( "%s task success" % self._timers_param) return False return True while continue_flag(): await uasyncio.sleep(1) now = utime.time() current = self._power_monitor.current( ) if self._power_monitor else None if current: self.log_current(current) expired += now - prev_time prev_time = now if current and current > self._reverse_threshold: await self.engine_reverse(True) await uasyncio.sleep(self._reverse_duration) expired -= self._reverse_duration + 2 * self._reverse_delay retries += 1 await self.engine_reverse(False) self.off(source=timer) if timer.time_on > time: break manage_memory() await uasyncio.sleep(60 - machine.RTC().now()[6])
def load_srv_json(file, srv_url=None): global HTTP_CLIENT if not srv_url: srv_url = updates_url() file_url = srv_url + file + '.json' try: if not HTTP_CLIENT: HTTP_CLIENT = HttpClient() return HTTP_CLIENT.get_json(file_url) except Exception as exc: LOG.exc(exc, 'Error loading server data: %s' % file) return None finally: manage_memory()
async def read(self, once=False): while True: for sensor_device in self.sensor_devices: if sensor_device.sensor_type == 'ds18x20': sensor_device.convert() await uasyncio.sleep_ms(self._sleep) for sensor_device in self.sensor_devices: sensor_device.read() if self.switches: self.adjust_switches() manage_memory() if once: break
async def srv_post(self, url, data, retry=False): if self.busy: return False data['device_id'] = self.id['id'] data['token'] = self.id['token'] manage_memory() machine.resetWDT() result = await self._http.post(self.server_uri + url, data) if retry: while not result: machine.resetWDT() result = await self._http.post(self.server_uri + url, data) machine.resetWDT() manage_memory() return result
async def adjust_switch(self, once=False): while True: now_tuple = machine.RTC().now() now = time_tuple_to_seconds(now_tuple, seconds=True) next_time_on = None if self._schedule_params: day = self.device.schedule.current_day() if day: limits = [ day[idx] if idx and day[idx] else None for idx in self._schedule_params_idx ] if limits[0] and ((limits[0] <= now and not limits[1]) or (limits[0] <= now < limits[1]) or (limits[0] < limits[1] <= now)): self.set_gate_state(1) if limits[1] and ((limits[1] <= now and not limits[0]) or (limits[1] <= now < limits[0]) or (limits[1] < limits[0] <= now)): self.set_gate_state(0) else: if now == 0: self.init_timers() passed_timers = [ timer for timer in self.timers if timer.time_on <= now ] if not passed_timers and self.timers: passed_timers = [self.timers[-1]] if passed_timers: last_timer = passed_timers[-1] await self.set_gate_state(1 if last_timer.duration == 0 else 0) next_timers = [ timer for timer in self.timers if timer.time_on > now ] if next_timers and last_timer.time_on < next_timers[ 0].time_on < last_timer.time_on + 60: next_time_on = next_timers[ 0].time_on - last_timer.time_on if once: break manage_memory() if next_time_on: await uasyncio.sleep(next_time_on) else: await uasyncio.sleep(60 - machine.RTC().now()[5])
def on(self, value=True, source='manual', manual=False): if manual: source = 'manual' if value: self._active[source] = True else: if source in self._active: del self._active[source] if self.state != bool(self._active): if self.state: self.reverse = False self.device.busy = self._active RelaySwitchController.on(self, self._active, source == 'manual') if 'manual' in self._active and len( self._active) == 1 and self._power_monitor: uasyncio.get_event_loop().create_task(self.check_current()) LOG.debug('Feeder state: %s' % self.state) manage_memory()
def perform_software_update(): global HTTP_CLIENT if not HTTP_CLIENT: HTTP_CLIENT = HttpClient() machine.WDT() manage_memory() version_data = load_version() device_type = get_device_type() srv_url = updates_url() srv_index = load_srv_json('index', srv_url=srv_url) machine.resetWDT() srv_versions = load_srv_json('devices', srv_url=srv_url) machine.resetWDT() if srv_index: for path, entry in srv_index.items(): if 'devices_types' in entry and 'base' not in entry[ 'devices_types'] and device_type not in entry[ 'devices_types']: continue if path in version_data['files'] and version_data['files'][ path] == entry['hash']: continue local_path = entry['path'] if 'path' in entry else path print(local_path) ensure_file_path(local_path) file_url = srv_url + 'software/' + path print(file_url) while version_data['files'].get(path) != entry['hash']: if HTTP_CLIENT.get_to_file(file_url, 'file_buf'): try: uos.remove(local_path) except OSError: pass uos.rename('file_buf', local_path) if version_data['hash']: version_data['hash'] = None version_data['files'][path] = entry['hash'] save_version(version_data) print('complete') machine.resetWDT() version_data['hash'] = srv_versions[device_type] version_data['update'] = False save_version(version_data) machine.reset()
async def post_log(self): while True: await uasyncio.sleep(59) try: if self.log_queue and not self.status['srv_req_pending']: while self.log_queue: entries_count = 10 if len( self.log_queue) > 10 else len(self.log_queue) entries = self.log_queue[:entries_count] rsp = await self.srv_post('devices_log/post', {'entries': entries}) if rsp: self.log_queue = self.log_queue[ entries_count:] if entries_count < len( self.log_queue) else [] else: break except Exception as exc: LOG.exc(exc, 'Server log post error') manage_memory()
async def check_updates(self, once=False): while True: deepsleep = bool(self.deepsleep()) timezone = self.settings.get('timezone') try: data = { 'schedule': { 'hash': self.schedule.hash, 'start': self.schedule.start }, 'props': self.settings } updates = await self.srv_post('device_updates', data, retry=once) if updates: if updates.get('schedule'): self.schedule.update(updates['schedule']) if updates.get('props'): self.settings = updates['props'] self.save_settings() for ctrl in self.modules.values(): if ctrl: ctrl.update_settings() if deepsleep != bool(self.deepsleep()): machine.reset() if timezone != self.settings.get('timezone'): self.ntp_sync() if 'mode' in self.settings and self.mode != self.settings[ 'mode']: machine.reset() except Exception as exc: LOG.exc(exc, 'Server updates check error') manage_memory() if once: break await uasyncio.sleep(30)
async def get_index(req, rsp): await APP.sendfile(rsp, 'html/index.html', content_type="text/html; charset=utf-8") manage_memory()
async def send_json(rsp, data): await picoweb.start_response(rsp, 'application/json', "200", {'cache-control': 'no-store'}) await rsp.awrite(ujson.dumps(data).encode('UTF-8')) manage_memory()
machine.resetWDT() LOOP.create_task(wdt_feed()) machine.WDT(True) NETWORK_CONTROLLER = NetworkController() if NETWORK_CONTROLLER.online(): software_version = load_json('version.json') if software_version and software_version['update']: from software_update import perform_software_update perform_software_update() machine.reset() manage_memory() DEVICE = LenferDevice(NETWORK_CONTROLLER) manage_memory() import lib.picoweb as picoweb APP = picoweb.WebApp(__name__) async def send_json(rsp, data): await picoweb.start_response(rsp, 'application/json', "200", {'cache-control': 'no-store'}) await rsp.awrite(ujson.dumps(data).encode('UTF-8')) manage_memory()
def __init__(self, network_controller): LOG.info("LenferDevice init") self._schedule = None self._network = network_controller self.mode = None self.status = { "wlan": None, "factory_reset": False, "wlan_switch": False, "ssid_failure": False, "ssid_delay": False, "srv_req_pending": False } self.log_queue = [] self.busy = False self._conf = load_json('conf.json') self.settings = load_json('settings.json') self._http = HttpClient() if not self.settings: self.load_def_settings() if self.settings.get('mode'): self.mode = self.settings['mode'] if self._conf.get('wlan_switch'): self._wlan_switch_button = Pin(self._conf['wlan_switch'], Pin.IN, Pin.PULL_UP, handler=self.wlan_switch_irq, trigger=Pin.IRQ_FALLING) if self._conf.get('factory_reset'): self._factory_reset_button = Pin(self._conf['factory_reset'], Pin.IN, handler=self.factory_reset_irq, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING) self.modules = {} self.i2c = [ I2C(scl=Pin(i2c_conf['scl']), sda=Pin(i2c_conf['sda'])) for i2c_conf in self._conf['i2c'] ] LOG.info('I2C init') self.leds = { led: Pin(pin_no, Pin.OUT) for led, pin_no in self._conf['leds'].items() } self.id = load_json('id.json') if 'debug' in self.id and self.id['debug']: self.server_uri = SERVER_URI_DEV else: self.server_uri = SERVER_URI for led in self.leds.values(): led.value(0) self.schedule = Schedule() manage_memory() machine.resetWDT() if 'rtc' in self._conf['modules'] and self.module_enabled( self._conf['modules']['rtc']): try: from timers import RtcController self.modules['rtc'] = RtcController( self, self._conf['modules']['rtc']) self.modules['rtc'].get_time(set_rtc=True) LOG.info('RTC init') except Exception as exc: LOG.exc(exc, 'RTC initialization error') machine.resetWDT() manage_memory() if 'climate' in self._conf['modules'] and self.module_enabled( self._conf['modules']['climate']): try: from climate import ClimateController self.modules['climate'] = ClimateController( self, self._conf['modules']['climate']) LOG.info('ClimateController init') except Exception as exc: LOG.exc(exc, 'Climate initialization error') if self._conf['modules']['climate'].get('obligatory'): LOG.error( 'Obligatory module initialization fail -- machine reset' ) machine.reset() machine.resetWDT() manage_memory() if 'power_monitor' in self._conf['modules'] and self.module_enabled( self._conf['modules']['power_monitor']): try: from power_monitor_controller import PowerMonitor self.modules['power_monitor'] = PowerMonitor( self, self._conf['modules']['power_monitor']) LOG.info('PowerMonitor init') except Exception as exc: LOG.exc(exc, 'PowerMonitor initialization error') if self._conf['modules']['power_monitor'].get('obligatory'): LOG.error( 'Obligatory module initialization fail -- machine reset' ) machine.reset() machine.resetWDT() manage_memory() if 'feeder' in self._conf['modules'] and self.module_enabled( self._conf['modules']['feeder']): try: from feeder import FeederController self.modules['feeder'] = FeederController( self, self._conf['modules']['feeder']) LOG.info('Feeder init') except Exception as exc: LOG.exc(exc, 'Feeder initialization error') machine.resetWDT() manage_memory() if 'gate' in self._conf['modules'] and self.module_enabled( self._conf['modules']['gate']): try: from gate_controller import GateController self.modules['gate'] = GateController( self, self._conf['modules']['gate']) LOG.info('Gate init') except Exception as exc: LOG.exc(exc, 'Gate initialization error') machine.resetWDT() manage_memory() if 'relay_switch' in self._conf['modules'] and self.module_enabled( self._conf['modules']['relay_switch']): try: from relay_switch import RelaySwitchController self.modules['relay_switch'] = RelaySwitchController( self, self._conf['modules']['relay_switch']) LOG.info('Relay init') except Exception as exc: LOG.exc(exc, 'RelaySwitch initialization error') machine.resetWDT() manage_memory() LOG.info(self.modules)
def adjust_switches(self): state = {} schedule_day = self.device.schedule.current_day() for param in self.sensors_roles: state[param] = { 'value': [ self.data[sensor_idx] for sensor_idx in self.sensors_roles[param] if self.data[sensor_idx] != None ], 'limits': [] } if state[param]['value']: param_value, param_delta = None, None if schedule_day: param_idx = self.device.schedule.param_idx(param) if param_idx != -1: param_value = schedule_day[param_idx] param_delta = self.device.schedule.params['delta'][ param_idx] if param_value == None or param_delta == None: if self.device.settings.get(param): param_value, param_delta = self.device.settings[param] if param_value != None and param_delta != None: state[param]['limits'] = [ param_value - param_delta, param_value + param_delta, ] if state.get('temperature') and state['temperature']['value']: if self.switches['heat']['enabled'] and state['temperature'][ 'limits']: if state['temperature']['value'][0] < state['temperature'][ 'limits'][0]: if not self.switches['heat']['pin'].value(): self.switches['heat']['pin'].value(1) LOG.info('Heat on') else: if self.switches['heat']['pin'].value(): self.switches['heat']['pin'].value(0) LOG.info('Heat off') if self.switches['vent_mix']['enabled']: if len(state['temperature']['value']) > 1: if state['temperature']['value'][0] > state['temperature']['value'][1] + 3 or\ state['temperature']['value'][0] < state['temperature']['value'][1] - 3: if not self.switches['vent_mix']['pin'].value(): self.switches['vent_mix']['pin'].value(1) LOG.info('Mix on') elif state['temperature']['value'][0] < state['temperature']['value'][1] + 1 and\ state['temperature']['value'][0] > state['temperature']['value'][1] - 1: if self.switches['vent_mix']['pin'].value(): self.switches['vent_mix']['pin'].value(0) LOG.info('Mix off') if self.switches['vent_mix']['enabled']: if not state.get('temperature') or len( state['temperature']['value']) < 2: if self.switches['vent_mix']['pin'].value(): self.switches['vent_mix']['pin'].value(0) LOG.info('Mix off') if self.switches['vent_out']['enabled']: if (state.get('humidity') and state['humidity']['value'] and state['humidity']['limits'] and\ state['humidity']['value'][0] > state['humidity']['limits'][1]) or\ (state.get('temperature') and state['temperature']['value'] and state['temperature']['limits'] and\ state['temperature']['value'][0] > state['temperature']['limits'][1]) or\ (state.get('co2') and state['co2']['value'][0] > ClimateController.CO2_THRESHOLD): if not self.switches['vent_out']['pin'].value(): self.switches['vent_out']['pin'].value(1) LOG.info('Out on') else: if self.switches['vent_out']['pin'].value(): self.switches['vent_out']['pin'].value(0) LOG.info('Out off') if self.switches['humid']['enabled']: if state.get('humidity') and state['humidity']['value'] and state['humidity']['limits'] and\ state['humidity']['value'][0] < state['humidity']['limits'][0]: if not self.switches['humid']['pin'].value(): self.switches['humid']['pin'].value(1) LOG.info('Humid on') else: if self.switches['humid']['pin'].value(): self.switches['humid']['pin'].value(0) LOG.info('Humid off') if self.switches['air_con']['enabled']: if state.get('temperature') and state['temperature']['value'] and state['temperature']['limits'] and\ state['temperature']['value'][0] > state['temperature']['limits'][1] + 3: if not self.switches['air_con']['pin'].value(): self.switches['air_con']['pin'].value(1) LOG.info('Air con on') if self.switches['vent_out']['enabled'] and self.switches['vent_out']['pin'].value()\ and (not state.get('co2') or not state['co2']['value'] or state['co2']['value'][0] < ClimateController.CO2_THRESHOLD): self.switches['vent_out']['pin'].value(0) else: if self.switches['air_con']['pin'].value(): self.switches['air_con']['pin'].value(0) LOG.info('Air con off') manage_memory()