# -*- coding: utf-8 -*-
"""The Raspberry tutorial
"""
__license__ = """
This file is part of Janitoo.
Janitoo is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Janitoo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Janitoo. If not, see <http://www.gnu.org/licenses/>.
"""
__author__ = 'Sébastien GALLET aka bibi21000'
__email__ = 'bibi21000@gmail.com'
__copyright__ = "Copyright © 2013-2014-2015-2016 Sébastien GALLET aka bibi21000"
import logging
logger = logging.getLogger(__name__)
import os, sys
import threading
import datetime
from janitoo.fsm import HierarchicalMachine as Machine
from janitoo.thread import JNTBusThread, BaseThread
from janitoo.options import get_option_autostart
from janitoo.utils import HADD
from janitoo.node import JNTNode
from janitoo.value import JNTValue
from janitoo.component import JNTComponent
from janitoo.bus import JNTBus
from janitoo_raspberry_dht.dht import DHTComponent
from janitoo_raspberry_gpio.gpio import GpioBus, OutputComponent, PirComponent as GPIOPir, LedComponent as GPIOLed, SonicComponent
from janitoo_raspberry_1wire.bus_1wire import OnewireBus
from janitoo_raspberry_1wire.components import DS18B20
from janitoo_hostsensor_raspberry.component import HardwareCpu
OID = 'tutorial3'
[docs]def make_ambiance(**kwargs):
return AmbianceComponent(**kwargs)
[docs]def make_led(**kwargs):
return LedComponent(**kwargs)
[docs]def make_temperature(**kwargs):
return TemperatureComponent(**kwargs)
[docs]def make_cpu(**kwargs):
return CpuComponent(**kwargs)
[docs]class TutorialBus(JNTBus):
"""A bus to manage Tutorial
"""
states = [
'booting',
'sleeping',
'reporting',
'ringing',
]
"""The tutorial states :
"""
transitions = [
{ 'trigger': 'wakeup',
'source': 'sleeping',
'dest': 'reporting',
'conditions': 'condition_values',
},
{ 'trigger': 'report',
'source': '*',
'dest': 'reporting',
'conditions': 'condition_values',
},
{ 'trigger': 'sleep',
'source': '*',
'dest': 'sleeping',
},
{ 'trigger': 'ring',
'source': 'reporting',
'dest': 'ringing',
'conditions': 'condition_values',
},
]
"""The transitions
"""
def __init__(self, **kwargs):
"""
"""
JNTBus.__init__(self, **kwargs)
self.buses = {}
self.buses['gpiobus'] = GpioBus(masters=[self], **kwargs)
self.buses['1wire'] = OnewireBus(masters=[self], **kwargs)
self._statemachine = None
self.check_timer = None
uuid="{:s}_timer_delay".format(OID)
self.values[uuid] = self.value_factory['config_float'](options=self.options, uuid=uuid,
node_uuid=self.uuid,
help='The delay between 2 checks',
label='Timer.',
default=30,
)
uuid="{:s}_temperature_critical".format(OID)
self.values[uuid] = self.value_factory['config_float'](options=self.options, uuid=uuid,
node_uuid=self.uuid,
help='The critical temperature. If 2 of the 3 temperature sensors are up to this value, a security notification is sent.',
label='Critical',
default=50,
)
uuid="{:s}_temperature".format(OID)
self.values[uuid] = self.value_factory['sensor_temperature'](options=self.options, uuid=uuid,
node_uuid=self.uuid,
help='The average temperature of tutorial.',
label='Temp',
)
poll_value = self.values[uuid].create_poll_value(default=300)
self.values[poll_value.uuid] = poll_value
uuid="{:s}_overheat".format(OID)
self.values[uuid] = self.value_factory['sensor_boolean'](options=self.options, uuid=uuid,
node_uuid=self.uuid,
help='Temperature overheat.',
label='Overheat',
default=False,
)
poll_value = self.values[uuid].create_poll_value(default=60)
self.values[poll_value.uuid] = poll_value
uuid="{:s}_transition".format(OID)
self.values[uuid] = self.value_factory['transition_fsm'](options=self.options, uuid=uuid,
node_uuid=self.uuid,
list_items=[ v['trigger'] for v in self.transitions ],
fsm_bus=self,
)
poll_value = self.values[uuid].create_poll_value()
self.values[poll_value.uuid] = poll_value
uuid="{:s}_state".format(OID)
self.values[uuid] = self.value_factory['sensor_string'](options=self.options, uuid=uuid,
node_uuid=self.uuid,
genre=0x01,
help='The state of the fsm.',
get_data_cb = self.get_state,
label='State',
)
poll_value = self.values[uuid].create_poll_value(default=60)
self.values[poll_value.uuid] = poll_value
self._bus_lock = threading.Lock()
self.presence_events = {}
self.state = "sleeping"
[docs] def get_state(self, node_uuid, index):
"""Get the state of the fsm
"""
return self.state
@property
def polled_sensors(self):
"""
"""
return [
self.nodeman.find_value('temperature', 'temperature'),
self.nodeman.find_value('ambiance', 'temperature'),
self.nodeman.find_value('ambiance', 'humidity'),
self.nodeman.find_value('cpu', 'temperature'),
self.nodeman.find_value('cpu', 'voltage'),
self.nodeman.find_value('cpu', 'frequency'),
self.nodeman.find_value('led', 'switch'),
self.nodeman.find_value('led', 'blink'),
self.nodeman.find_bus_value('temperature'),
self.nodeman.find_bus_value('transition'),
self.nodeman.find_bus_value('overheat'),
]
[docs] def condition_values(self):
"""
"""
logger.debug("[%s] - condition_values", self.__class__.__name__)
return all(v is not None for v in self.polled_sensors)
[docs] def on_enter_reporting(self):
"""
"""
logger.debug("[%s] - on_enter_reporting", self.__class__.__name__)
self.bus_acquire()
try:
self.nodeman.find_value('led', 'blink').data = 'heartbeat'
self.nodeman.add_polls(self.polled_sensors, slow_start=True, overwrite=False)
#In sleeping mode, send the state of the fsm every 900 seconds
#We update poll_delay directly to not update the value in configfile
state = self.nodeman.find_bus_value('state')
state.poll_delay = self.nodeman.find_bus_value('state_poll').data
overheat = self.nodeman.find_bus_value('overheat')
overheat.poll_delay = self.nodeman.find_bus_value('overheat_poll').data
self.nodeman.publish_value(overheat)
self.nodeman.add_polls([state, overheat], slow_start=True, overwrite=True)
except Exception:
logger.exception("[%s] - Error in on_enter_reporting", self.__class__.__name__)
finally:
self.bus_release()
[docs] def on_enter_sleeping(self):
"""
"""
logger.debug("[%s] - on_enter_sleeping", self.__class__.__name__)
self.bus_acquire()
try:
self.stop_check()
self.nodeman.remove_polls(self.polled_sensors)
self.nodeman.find_value('led', 'blink').data = 'off'
#In sleeping mode, send the state of the fsm every 900 seconds
#We update poll_delay directly to nto update the value in config file
self.nodeman.find_bus_value('state').poll_delay = 900
except Exception:
logger.exception("[%s] - Error in on_enter_sleeping", self.__class__.__name__)
finally:
self.bus_release()
[docs] def on_exit_sleeping(self):
"""
"""
logger.debug("[%s] - on_exit_sleeping", self.__class__.__name__)
self.on_check()
[docs] def on_enter_ringing(self):
"""
"""
logger.debug("[%s] - on_enter_ringing", self.__class__.__name__)
self.bus_acquire()
try:
self.nodeman.find_value('led', 'blink').data = 'warning'
#In sleeping mode, send the state of the fsm every 900 seconds
#We update poll_delay directly to not update the value in configfile
state = self.nodeman.find_bus_value('state')
state.poll_delay = 1.0 * self.nodeman.find_bus_value('state_poll').data / 3
overheat = self.nodeman.find_bus_value('overheat')
overheat.poll_delay = 1.0 * self.nodeman.find_bus_value('overheat_poll').data / 3
self.nodeman.publish_value(overheat)
self.nodeman.add_polls([state, overheat], slow_start=True, overwrite=True)
except Exception:
logger.exception("[%s] - Error in on_enter_ringing", self.__class__.__name__)
finally:
self.bus_release()
[docs] def bus_acquire(self, blocking=True):
"""Get a lock on the bus"""
if self._bus_lock.acquire(blocking):
return True
return False
[docs] def bus_release(self):
"""Release a lock on the bus"""
self._bus_lock.release()
[docs] def bus_locked(self):
"""Get status of the lock"""
return self._bus_lock.locked()
[docs] def stop_check(self):
"""Check that the component is 'available'
"""
if self.check_timer is not None:
self.check_timer.cancel()
self.check_timer = None
[docs] def on_check(self):
"""Make a check using a timer.
"""
self.bus_acquire()
try:
self.stop_check()
if self.check_timer is None and self.is_started:
timer_delay = self.get_bus_value('timer_delay').data
if self.state == 'ringing':
timer_delay = 1.0 * timer_delay / 2
self.check_timer = threading.Timer(timer_delay, self.on_check)
self.check_timer.start()
finally:
self.bus_release()
try:
state = True
#Check the temperatures
critical_temp = self.get_bus_value('temperature_critical').data
criticals = 0
nums = 0
total = 0
mini = maxi = None
for value in [('temperature', 'temperature'), ('ambiance', 'temperature'), ('cpu', 'temperature')]:
data = self.nodeman.find_value(*value).data
if data is None:
#We should notify a sensor problem here.
pass
else:
nums += 1
total += data
if data > critical_temp:
criticals += 1
if maxi is None or data > maxi:
maxi = data
if mini is None or data < mini:
mini = data
if criticals > 1:
if self.state != 'ringing':
#We should notify a security problem : fire ?
self.nodeman.find_bus_value('overheat').data = True
self.ring()
elif self.state == 'ringing':
#We should notify a security problem : fire ?
self.nodeman.find_bus_value('overheat').data = False
self.report()
if nums != 0:
self.get_bus_value('temperature').data = total / nums
except Exception:
logger.exception("[%s] - Error in on_check", self.__class__.__name__)
[docs] def start(self, mqttc, trigger_thread_reload_cb=None):
"""Start the bus
"""
for bus in self.buses:
self.buses[bus].start(mqttc, trigger_thread_reload_cb=None)
JNTBus.start(self, mqttc, trigger_thread_reload_cb)
self._statemachine = self.create_fsm()
[docs] def create_fsm(self):
"""Create the fsm
"""
if hasattr(self, "get_graph"):
delattr(self, "get_graph")
return Machine(self,
states=self.states,
transitions=self.transitions,
title='Bus',
initial='booting')
[docs] def stop(self):
"""Stop the bus
"""
self.stop_check()
self.sleep()
for bus in self.buses:
self.buses[bus].stop()
JNTBus.stop(self)
[docs] def check_heartbeat(self):
"""Check that the component is 'available'
"""
res = False
#~ for bus in self.buses:
#~ res = res and self.buses[bus].check_heartbeat()
logger.debug("[%s] - sensors %s", self.__class__.__name__, self.polled_sensors)
if self.state == 'booting' and all(v is not None for v in self.polled_sensors):
#We try to enter in sleeping mode
self.sleep()
return self.state != 'booting'
[docs] def loop(self, stopevent):
"""Retrieve data
Don't do long task in loop. Use a separated thread to not perturbate the nodeman
"""
for bus in self.buses:
self.buses[bus].loop(stopevent)
[docs]class AmbianceComponent(DHTComponent):
""" A component for ambiance """
def __init__(self, bus=None, addr=None, **kwargs):
"""
"""
oid = kwargs.pop('oid', '%s.ambiance'%OID)
name = kwargs.pop('name', "Ambiance sensor")
DHTComponent.__init__(self, oid=oid, bus=bus, addr=addr, name=name,
**kwargs)
logger.debug("[%s] - __init__ node uuid:%s", self.__class__.__name__, self.uuid)
[docs]class LedComponent(GPIOLed):
""" A component for a Led (on/off) """
def __init__(self, bus=None, addr=None, **kwargs):
"""
"""
oid = kwargs.pop('oid', '%s.led'%OID)
name = kwargs.pop('name', "Led")
GPIOLed.__init__(self, oid=oid, bus=bus, addr=addr, name=name,
**kwargs)
logger.debug("[%s] - __init__ node uuid:%s", self.__class__.__name__, self.uuid)
[docs]class TemperatureComponent(DS18B20):
""" A water temperature component """
def __init__(self, bus=None, addr=None, **kwargs):
"""
"""
oid = kwargs.pop('oid', '%s.temperature'%OID)
name = kwargs.pop('name', "Temperature")
DS18B20.__init__(self, oid=oid, bus=bus, addr=addr, name=name,
**kwargs)
logger.debug("[%s] - __init__ node uuid:%s", self.__class__.__name__, self.uuid)
[docs]class CpuComponent(HardwareCpu):
""" A water temperature component """
def __init__(self, bus=None, addr=None, **kwargs):
"""
"""
oid = kwargs.pop('oid', '%s.cpu'%OID)
name = kwargs.pop('name', "CPU")
HardwareCpu.__init__(self, oid=oid, bus=bus, addr=addr, name=name,
**kwargs)
logger.debug("[%s] - __init__ node uuid:%s", self.__class__.__name__, self.uuid)