class WebCameraControl(LoopTask): """ Heroku CloundMQTTを購読し、Web Cameraを操作する """ def __init__(self): super(WebCameraControl, self).__init__() def setup(self): try: self.proxy = appservice.ServiceProxy() proxy = appservice.ServiceProxy() url = proxy.get_account("heroku")["CLOUDMQTT_URL"] url = urlparse.urlparse(url) self.mqttc = MqttClient(url.hostname, url.port) self.mqttc.client.username_pw_set(url.username, url.password) self.mqttc.subscribe("automation/camera/+", self.callback) self.cmdq = Queue() logger.info("setup: done.") except: logger.critical("unhandled exception") raise def loop(self): try: self.mqttc.secure_connection() cmd = self.cmdq.get() if cmd == "send": self.send_mail() self.cmdq.task_done() logger.info("loop: cmd={} done.".format(cmd)) except: logger.error("unhandled exception") def callback(self, topic, payload): try: logger.info("callback: topic={}, payload={}".format(topic, payload)) self.cmdq.put(topic.split("/")[-1]) except: logger.error("unhandled exception") def send_mail(self): with tempfile.NamedTemporaryFile(suffix=".png") as imgfile: cam = webcam.OpenCVCamera() # must be initialized in main thread cam.capture_to_file(imgfile.name) self.proxy.sendmail_with_media( self.proxy.get_account("personal")["email"], "Live Image by Web Camera", str(datetime.datetime.now()), xmlrpclib.Binary(imgfile.read()), )
class EnvironmentMonitor(LoopTask): ''' MQTTにより、センサの値を購読し、Redis(RPi, Heroku)に登録する ''' def setup(self): try: self.last_access = {} self.mqttc = MqttClient() self.mqttc.subscribe('sensors/+/+', self.callback) self.rs = redis.StrictRedis(host=config.raspi2, port=6379, db=0) proxy = appservice.ServiceProxy() self.heroku = proxy.get_account('heroku')['URL'] logger.info('setup: done.') except: logger.critical('unhandled exception') raise def loop(self): try: self.mqttc.secure_connection() self.watchdog() time.sleep(10) except: logger.error('unhandled exception') def callback(self, topic, payload): logger.debug('callback(): topic="{}", payload="{}"'.format(topic, payload)) # print topic, payload try: path = topic.split('/') vtype = path[-1] client = path[-2] value = payload timestamp = datetime.datetime.now() # Register access time self.last_access[client] = timestamp # Upload data self.store(topic, value) self.store(topic + '/datetime', str(timestamp)) except: logger.error('unhandled exception') def watchdog(self): proxy = appservice.ServiceProxy() for client, last in self.last_access.items(): tdiff = datetime.datetime.now() - last if tdiff.total_seconds() <= 60 * 60: continue msg = '{} is inactive.'.format(client) logger.warning(msg) proxy.line_me({'value1': msg}) self.last_access[client] = datetime.datetime.now() def store(self, topic, value): # Redis self.rs.set(topic, value) # Heroku upload = '{}/kv/{}'.format(self.heroku, topic) r = requests.put(url=upload, data=value) if r.status_code != 200: logger.error('status_code != 200: upload={}, date={}'.format(upload, value))
class SolarMonitor(LoopTask): ''' MQTT topic solar/values を購読し、値をRedis(RPi, Heroku)に登録する 登録した値は、Munin, Heroku Webにより利用される ''' def setup(self): try: self.mqttc = MqttClient() self.mqttc.subscribe('solar/values', self.callback) self.rs = redis.StrictRedis(host=config.raspi2, port=6379, db=0) proxy = appservice.ServiceProxy() self.heroku = proxy.get_account('heroku')['URL'] logger.info('setup: done.') except: logger.critical('unhandled exception') raise def loop(self): try: self.mqttc.secure_connection() time.sleep(10) except: logger.error('unhandled exception') def callback(self, topic, payload): ''' 瞬時の電圧・電流・電力から、積算電力も算出して、Redisに登録 ''' try: cur_values = json.loads(payload) prev_values = dict(cur_values) prev = self.rs.get(topic) if prev: prev_values = json.loads(prev) cur_time = cur_values.get('Time', time.time()) prev_time = prev_values.get('Time', cur_time) dt = (cur_time - prev_time) / 60.0 / 60.0 # hour accum_pp = prev_values.get('AccumSolarPower', 0.0) accum_bp = prev_values.get('AccumBatteryPower', 0.0) accum_cp = prev_values.get('AccumLoadPower', 0.0) accum_pp += (prev_values['SolarPower'] + cur_values['SolarPower']) * dt / 2.0 accum_bp += (prev_values['BatteryPower'] + cur_values['BatteryPower']) * dt / 2.0 accum_cp += (prev_values['LoadPower'] + cur_values['LoadPower']) * dt / 2.0 cur_values['AccumSolarPower'] = accum_pp cur_values['AccumBatteryPower'] = accum_bp cur_values['AccumLoadPower'] = accum_cp # Redisに保存するデータは、数値型を維持するためにJSON形式とした # (Redisには、数値型がない) timestamp = str(datetime.datetime.now()) values = json.dumps(cur_values) self.rs.set(topic, values) self.rs.set(topic + '/datetime', timestamp) upload = '{0}/kv/{1}'.format(self.heroku, topic) r = requests.put(url=upload, data=values) if r.status_code != 200: logger.warning('status_code != 200: upload={}, data={}'.format(upload, value)) upload = upload + '/datetime' r = requests.put(url=upload, data=timestamp) if r.status_code != 200: logger.warning('status_code != 200: upload={}, data={}'.format(upload, timestamp)) except: logger.error('unhandled exception')
class AirControl(LoopTask): ''' Heroku CloundMQTTを購読し、エアコンを操作する ''' def __init__(self): super(AirControl, self).__init__() def setup(self): try: self.ac = AirConditioner() self.proxy = appservice.ServiceProxy() url = self.proxy.get_account('heroku')['CLOUDMQTT_URL'] url = urlparse.urlparse(url) self.mqttc = MqttClient(url.hostname, url.port) self.mqttc.client.username_pw_set(url.username, url.password) self.mqttc.subscribe('automation/ac/stat', self.callback) self.cmdq = Queue() logger.info('setup: done.') except: logger.critical('unhandled exception') raise def loop(self): try: self.mqttc.secure_connection() cmd = self.cmdq.get() func = lambda: None msg = None if cmd == 'on': func = self.ac.on msg = u'エアコンをオンしました。' if cmd == 'off': func = self.ac.off msg = u'エアコンをオフしました。' for _ in range(3): func() time.sleep(3) self.cmdq.task_done() logger.info('loop: cmd={} done.'.format(cmd)) if msg: self.proxy.line_family(msg) except: logger.error('unhandled exception') def callback(self, topic, payload): try: logger.info('callback: topic={}, payload={}'.format(topic, payload)) if len(payload) == 0: return if payload[0] == '1': self.cmdq.put('on') if payload[0] == '0': self.cmdq.put('off') except: logger.error('unhandled exception')