Esempio n. 1
0
    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self._uart = None
        self._pps_pin = None
        if kwargs is not None:
            if 'gps_uart' in kwargs:
                self._uart = kwargs['gps_uart']
            if 'pps_pin' in kwargs:
                self._pps_pin = kwargs['pps_pin']

        if (self._uart == None):
            raise ValueError("need a uart for the gps")
        if (self._pps_pin == None):
            raise ValueError("need a pin that gps sends 1pps to us on")

        # we also need the RTC device
        self._rtc = RTC()
        self._pps_event = Event()
        self._rtc_ssr = uctypes.struct(_RTC_BASE + _RTC_SSR_OFFSET,
                                       self._rtc_ssr_struct, uctypes.NATIVE)
        self._rtc_dr = uctypes.struct(_RTC_BASE + _RTC_DR_OFFSET,
                                      self._rtc_dr_struct, uctypes.NATIVE)
        self._pps_rtc = 0
        self._pps_discard = 0
        self._ss_offset = -10000
        self._refclk = (0, 0, 0, 0, 0, 0)
Esempio n. 2
0
 def __init__(self, pin, callback):
     self._ev_start = Event()
     self._callback = callback
     self._times = array('i',  (0 for _ in range(self.edgecount + 1))) # 1 for overrun
     ExtInt(pin, ExtInt.IRQ_RISING_FALLING, Pin.PULL_NONE, self._cb_pin)
     self._reset()
     loop = asyncio.get_event_loop()
     loop.create_task(self._run())
Esempio n. 3
0
class NEC_IR():
    edgecount = 68
    block_time = 73  # 68.1ms nominal. Allow for some tx tolerance (?)

    def __init__(self, pin, callback):
        self._ev_start = Event()
        self._callback = callback
        self._times = array(
            'i', (0 for _ in range(self.edgecount + 1)))  # 1 for overrun
        ExtInt(pin, ExtInt.IRQ_RISING_FALLING, Pin.PULL_NONE, self._cb_pin)
        self._reset()
        loop = asyncio.get_event_loop()
        loop.create_task(self._run())

    def _reset(self):
        self._edge = 0
        self._overrun = False
        self._ev_start.clear()

    async def _run(self):
        while True:
            await self._ev_start  # Wait unitl data collection has started
            await asyncio.sleep_ms(self.block_time
                                   )  # Data block should have ended
            self._decode()  # decode, clear event, prepare for new rx, call cb

    # Pin interrupt. Save time of each edge for later decode.
    def _cb_pin(self, line):
        if not self._overrun:  # Overrun: ignore pulses until software timer times out
            if not self._ev_start.is_set():  # First edge received
                self._ev_start.set()
            self._times[self._edge] = ticks_us()
            if self._edge < self.edgecount:
                self._edge += 1
            else:
                self._overrun = True  # Overrun. decode() will test and reset

    def _decode(self):
        val = -3 if self._overrun else 0
        if not self._overrun:
            width = ticks_diff(self._times[1], self._times[0])
            if width > 4000:  # 9ms leading mark for all valid data
                width = ticks_diff(self._times[2], self._times[1])
                if width > 3000:  # 4.5ms space for normal data
                    if self._edge < self.edgecount:
                        val = -1  # Haven't received the correct number of edges
                    else:  # Time spaces only (marks are identical)
                        for edge_no in range(3, self.edgecount, 2):
                            val &= 0x1fffffff  # Constrain to 32 bit integer (b30 == b31)
                            val <<= 1  # nos. will still be unique because of logical inverse address
                            width = ticks_diff(self._times[edge_no + 1],
                                               self._times[edge_no])
                            if width > 1120:
                                val += 1
                elif width > 1700:  # 2.5ms space for a repeat code. Should have exactly 4 edges.
                    val = 1 if self._edge == 4 else -2
        self._reset()
        self._callback(val)
Esempio n. 4
0
 def __init__(self, pin, callback):
     self._ev_start = Event()
     self._callback = callback
     self._times = array(
         'i', (0 for _ in range(self.edgecount + 1)))  # 1 for overrun
     pin.irq(handler=self._cb_pin,
             trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING))
     self._reset()
     loop = asyncio.get_event_loop()
     loop.create_task(self._run())
Esempio n. 5
0
 def __init__(self):
     # generic section
     self._log = core._log
     self._log.debug("Plugin: test contruction")
     self._utils = core._utils
     self._plugins = core._plugins
     self._lock = Event()
     # plugin specific section
     self.valuenames = {}
     self.valuenames["valueN1"] = "GPIO"
     self.valuenames["valueF1"] = ""
     self.valuenames["valueD1"] = 0
     # release lock, ready for next measurement
     self._lock.clear()
Esempio n. 6
0
async def run_event_test():
    print('Test Lock class')
    loop = asyncio.get_event_loop()
    lock = Lock()
    loop.create_task(run_lock(1, lock))
    loop.create_task(run_lock(2, lock))
    loop.create_task(run_lock(3, lock))
    print('Test Event class')
    event = Event()
    print('got here')
    loop.create_task(eventset(event))
    print('gh1')
    await eventwait(event)  # run_event_test runs fast until this point
    print('Event status {}'.format('Incorrect' if event.is_set() else 'OK'))
    print('Tasks complete')
Esempio n. 7
0
 def __init__(self) :
     # generic section
     self._log       = core._log
     self._log.debug("Plugin: ds18 contruction")
     self._utils     = core._utils
     self._plugins   = core._plugins
     self._hal       = core._hal
     self._lock      = Event()
     # plugin specific section
     self.dxpin      = dxpin
     self.valuenames["valueN1"]= "Temperature"
     self.valuenames["valueF1"]= ""
     self.valuenames["valueD1"]= 0
     # release lock, ready for next measurement
     self._lock.clear()
Esempio n. 8
0
 def __init__(self, pin, callback, extended, *args):  # Optional args for callback
     self._ev_start = Event()
     self._callback = callback
     self._extended = extended
     self._addr = 0
     self.block_time = 80 if extended else 73  # Allow for some tx tolerance (?)
     self._args = args
     self._times = array('i',  (0 for _ in range(_EDGECOUNT + 1)))  # +1 for overrun
     if platform == 'pyboard':
         ExtInt(pin, ExtInt.IRQ_RISING_FALLING, Pin.PULL_NONE, self._cb_pin)
     else:
         pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING), hard = True)
     self._edge = 0
     self._ev_start.clear()
     loop = asyncio.get_event_loop()
     loop.create_task(self._run())
Esempio n. 9
0
 def __init__(self):
     # generic section
     self._log = core._log
     self._log.debug("Plugin: bme280 contruction")
     self._utils = core._utils
     self._plugins = core._plugins
     self._lock = Event()
     # plugin specific section
     self.valuenames["valueN1"] = "Temperature"
     self.valuenames["valueN2"] = "Humidity"
     self.valuenames["valueN3"] = "Pressure"
     self.valuenames["valueF1"] = ""
     self.valuenames["valueF2"] = ""
     self.valuenames["valueF3"] = ""
     self.valuenames["valueD1"] = 0
     self.valuenames["valueD2"] = 0
     self.valuenames["valueD3"] = 0
     # release lock, ready for next measurement
     self._lock.clear()
Esempio n. 10
0
class LED_Flashable():
    def __init__(self, loop, led_no):
        self.led = pyb.LED(led_no)
        self.event = Event()
        loop.create_task(self.flash_task())

    def flash(self, period_ms):
        if self.event.is_set():
            return
        self.event.set(period_ms)

    async def flash_task(self):
        while True:
            await self.event
            period_ms = self.event.value()
            self.event.clear()
            self.led.on()
            await asyncio.sleep_ms(period_ms)
            self.led.off()
Esempio n. 11
0
async def run_ack():
    loop = asyncio.get_event_loop()
    event = Event()
    ack1 = Event()
    ack2 = Event()
    count = 0
    while True:
        loop.create_task(event_wait(event, ack1, 1))
        loop.create_task(event_wait(event, ack2, 2))
        event.set(count)
        count += 1
        print('event was set')
        await ack1
        ack1.clear()
        print('Cleared ack1')
        await ack2
        ack2.clear()
        print('Cleared ack2')
        event.clear()
        print('Cleared event')
        await asyncio.sleep(1)
Esempio n. 12
0
 def __init__(self):
     self._log = core._log
     self._log.debug("Protocol: domoticz http contruction")
     self._lock = Event()
     # release lock, ready for next loop
     self._lock.clear()
Esempio n. 13
0
class Copernicus_GPS(gps.GPS):
    _enable_sentences = {
        'GGA': (1 << 0),
        'GLL': (1 << 1),
        'VTG': (1 << 2),
        'GSV': (1 << 3),
        'GSA': (1 << 4),
        'ZDA': (1 << 5),
        'RMC': (1 << 8),
        'TF': (1 << 9),
        'BA': (1 << 13)
    }

    class PPS_Mode:
        OFF = 0
        ON = 1
        FIX = 2

    class PPS_Polarity:
        ACTIVE_LOW = 0
        ACTIVE_HIGH = 1

    # we have some special events for config apply
    def __init__(self, uart):
        super().__init__(uart)
        self._nm_ack = Event()
        self._ps_ack = Event()

    # Additional incoming message processing (on to of gps.py)

    # PTNLRNM - Automatic Message Output (Response)
    async def _rx_ptnlrnm(self, segs):
        self._nm_ack.set()
        return

    # PTNLRPS - PPS Configuration (Response)
    async def _rx_ptnlrps(self, segs):
        self._ps_ack.set()
        return

    async def set_auto_messages(self, types, interval):
        # compute bitmask to enable messages
        bitmask = 0
        for type in types:
            bitmask |= self._enable_sentences[type]
        #print(bin(bitmask))
        # send the command
        fmt = 'PTNLSNM,{:04x},{:02d}'
        #print(fmt.format(bitmask,interval))
        print('gps: setting auto messages to', types, 'every', interval,
              'seconds')
        await self._send(fmt.format(bitmask, interval))
        # wait for ack
        print('gps: awaiting messages config ack')
        await self._nm_ack
        self._nm_ack.clear()
        print('gps: messages config accepted')
        return

    async def set_pps_mode(self, mode, length_ns, polarity, cable_ns):
        fmt = 'PTNLSPS,{},{},{},{}'
        # length is in 1/100th of ns
        length_ns = int(length_ns / 100)
        print('gps: setting PPS config')
        await self._send(fmt.format(mode, length_ns, polarity, cable_ns))
        print('gps: awaiting ack to PPS config')
        await self._ps_ack
        self._ps_ack.clear()
        print('gps: PPS config accepted')
        return
Esempio n. 14
0
 def __init__(self):
     self._log = core._log
     self._log.debug("Protocol: openhab mqtt contruction")
     self._lock = Event()
     # release lock, ready for next loop
     self._lock.clear()
Esempio n. 15
0
class switch_plugin:
    inputtype = "normal"  # Default switch type
    datastore = None  # Place where plugin data is stored for reboots

    def __init__(self):
        # generic section
        self._log = core._log
        self._log.debug("Plugin: switch contruction")
        self._utils = core._utils
        self._plugins = core._plugins
        self._hal = core._hal
        self._lock = Event()
        self.dxpin = dxpin
        # plugin specific section
        self.valuenames = {}
        self.valuenames["valueN1"] = "switch"
        self.valuenames["valueF1"] = ""
        self.valuenames["valueD1"] = 0
        # release lock, ready for next measurement
        self._lock.clear()

    def init(self, plugin, device, queue, scriptqueue):
        self._log.debug("Plugin: switch init")
        # generic section
        self._utils.plugin_initdata(self, plugin, device, queue, scriptqueue)
        self.content = plugin.get('content', content)
        plugin['dtype'] = dtype
        plugin['stype'] = stype
        plugin['template'] = template
        datastore = self._plugins.readstore(self.devicename)
        # plugin specific section
        self.switch_status = bootstate
        if self.inputtype == 'normal':
            self._log.debug("Plugin: switch init normal, pin: " + self.dxpin)
            # Setup switch
            self.swpin = self._hal.pin(self.dxpin, core.PIN_IN,
                                       core.PIN_PULL_UP)
            self.switch = Switch(self.swpin)
            # Register coros to launch on contact close and open
            self.switch.close_func(self.asyncswitchopen)
            self.switch.open_func(self.asyncswitchclosed)
        elif self.inputtype == 'low':
            self._log.debug("Plugin: switch init low, pin: " + self.dxpin)
            # Setup button active low
            self.swpin = self._hal.pin(self.dxpin, core.PIN_IN,
                                       core.PIN_PULL_UP)
            self.switch = Pushbutton(self.swpin)
            self.switch.press_func(self.asyncbuttonpress)
            self.switch.release_func(self.asyncbuttonrelease)
            self.switch.double_func(self.asyncbuttondouble)
            self.switch.long_func(self.asyncbuttonlong)
        else:
            self._log.debug("Plugin: switch init high, pin: " + self.dxpin)
            # Setup button active high
            self.swpin = self._hal.pin(self.dxpin, core.PIN_IN,
                                       core.PIN_PULL_DOWN)
            self.switch = Pushbutton(self.swpin)
            self.switch.press_func(self.asyncbuttonpress)
            self.switch.release_func(self.asyncbuttonrelease)
            self.switch.double_func(self.asyncbuttondouble)
            self.switch.long_func(self.asyncbuttonlong)
        return True

    def loadform(self, plugindata):
        self._log.debug("Plugin: switch loadform")
        # generic section
        self._utils.plugin_loadform(self, plugindata)
        # plugin specific section
        plugindata['inputtype'] = self.inputtype
        plugindata['dxpin0'] = self.dxpin

    def saveform(self, plugindata):
        self._log.debug("Plugin: switch saveform")
        # generic section
        self._utils.plugin_saveform(self, plugindata)
        # plugin specific section
        self.inputtype = plugindata['inputtype']
        self.dxpin = plugindata['dxpin0']

        # store values
        data = {}
        data["inputtype"] = self.inputtype
        data["dxpin"] = self.dxpin
        data["valueN1"] = self.valuenames["valueN1"]
        data["valueF1"] = self.valuenames["valueF1"]
        data["valueD1"] = self.valuenames["valueD1"]
        self._plugins.writestore(self.devicename, data)

        if self.inputtype == 'normal':
            # Setup switch
            self.swpin = self._hal.pin(self.dxpin, core.PIN_IN,
                                       core.PIN_PULL_UP)
            self.switch = Switch(self.swpin)
            # Register coros to launch on contact close and open
            self.switch.close_func(self.asyncswitchopen)
            self.switch.open_func(self.asyncswitchclosed)
        elif self.inputtype == 'low':
            # Setup button active low
            self.swpin = self._hal.pin(self.dxpin, core.PIN_IN,
                                       core.PIN_PULL_UP)
            self.switch = Pushbutton(self.swpin)
            self.switch.press_func(self.asyncbuttonpress)
            self.switch.release_func(self.asyncbuttonrelease)
            self.switch.double_func(self.asyncbuttondouble)
            self.switch.long_func(self.asyncbuttonlong)
        else:
            # Setup button active high
            self.swpin = self._hal.pin(self.dxpin, core.PIN_IN,
                                       core.PIN_PULL_DOWN)
            self.switch.press_func(self.asyncbuttonpress)
            self.switch.release_func(self.asyncbuttonrelease)
            self.switch.double_func(self.asyncbuttondouble)
            self.switch.long_func(self.asyncbuttonlong)

    def read(self, values):
        self._log.debug("Plugin: switch read")
        # generic section
        values['valueN1'] = self.valuenames["valueN1"]
        values['valueD1'] = self.switch_status

    def write(self, values):
        self._log.debug("Plugin: switch write")

    async def asyncprocess(self):
        self._log.debug("Plugin: switch process")
        # plugin specific section
        # If a switch occured
        if self.switch_status:
            # send data to protocol and script/rule queues
            self.valuenames["valueD1"] = self.switch_status
            self._utils.plugin_senddata(self)
            # erase status
            self.switch_status = ""
        # release lock, ready for next measurement
        self._lock.clear()

    #
    #CUSTOM SENSOR CODE...
    #

    async def asyncswitchopen(self):
        self.switch_status = 'open'
        # release lock, ready for next measurement
        self._lock.clear()

    async def asyncswitchclosed(self):
        self.switch_status = 'closed'
        # release lock, ready for next measurement
        self._lock.clear()

    async def asyncbuttonpress(self):
        self.switch_status = 'press'
        # release lock, ready for next measurement
        self._lock.clear()

    async def asyncbuttonrelease(self):
        self.switch_status = 'release'
        # release lock, ready for next measurement
        self._lock.clear()

    async def asyncbuttondouble(self):
        self.switch_status = 'double'
        # release lock, ready for next measurement
        self._lock.clear()

    async def asyncbuttonlong(self):
        self.switch_status = 'long'
        # release lock, ready for next measurement
        self._lock.clear()
Esempio n. 16
0
from gc import collect, mem_free
from sys import platform

from asyn import Event
from homie import __version__, utils
from homie.constants import (DEVICE_STATE, MAIN_DELAY, QOS, RESTORE_DELAY,
                             SLASH, UNDERSCORE)
from mqtt_as import MQTTClient
from uasyncio import get_event_loop, sleep_ms
from utime import time

_EVENT = Event()
def await_ready_state(func):
    def new_gen(*args, **kwargs):
        await _EVENT
        await func(*args, **kwargs)
    return new_gen


class HomieDevice:

    """MicroPython implementation of the Homie MQTT convention for IoT."""

    def __init__(self, settings):
        self._state = "init"
        self._stime = time()

        self.stats_interval = settings.DEVICE_STATS_INTERVAL

        self.nodes = []
        self.callback_topics = {}
Esempio n. 17
0
 def __init__(self, loop, led_no):
     self.led = pyb.LED(led_no)
     self.event = Event()
     loop.create_task(self.flash_task())
Esempio n. 18
0
class bme280_plugin:
    valuenames          = {}
    datastore           = None
    
    def __init__(self) :
        # generic section
        self._log       = core._log
        self._log.debug("Plugin: bme280 contruction")
        self._utils     = core._utils
        self._plugins   = core._plugins
        self._lock      = Event()
        # plugin specific section
        self.valuenames["valueV1"] = 0 # added by AJ
        self.valuenames["valueV2"] = 0 # added by AJ
        self.valuenames["valueV3"] = 0 # added by AJ
        self.valuenames["valueN1"]= "Temperature"
        self.valuenames["valueN2"]= "Humidity"
        self.valuenames["valueN3"]= "Pressure"
        self.valuenames["valueF1"]= ""
        self.valuenames["valueF2"]= ""
        self.valuenames["valueF3"]= ""
        self.valuenames["valueD1"]= 0
        self.valuenames["valueD2"]= 0
        self.valuenames["valueD3"]= 0
        # release lock, ready for next measurement
        self._lock.clear()
        
    def init(self, plugin, device, queue, scriptqueue):        
        self._log.debug("Plugin: bme280 init")
        # plugin generic section
        self._utils.plugin_initdata(self, plugin, device, queue, scriptqueue)
        self.content            = plugin.get('content',content)
        self.pincnt             = pincnt
        self.valuecnt           = valuecnt
        self.stype              = stype
        self.valuenames['devicename'] = device['name'] # gets device/plugin name, added AJ
        plugin['dtype']         = dtype
        plugin['stype']         = stype
        plugin['template']      = template
        datastore               = self._plugins.readstore(device["name"])
        # plugin specific section
        self.bme_elev           = bme_elev
        self.i2c_addr            = i2c_addr
        self.i2c                = core._hal.get_i2c(i2c)
        if self.i2c != None: 
            try:
                self._log.debug("Plugin: bme280 init i2c")
                self.bme280_init(address=self.i2c_addr)
            except OSError as e:
                self._log.debug("Plugin: bme280 init OSError exception: "+repr(e))
                return False
        return True

    def loadform(self,plugindata):
        self._log.debug("Plugin: bme280 loadform")
        # generic section
        self._utils.plugin_loadform(self, plugindata)
        plugindata['i2c']   = i2c
        plugindata['i2c_addr']   = self.i2c_addr
        # plugin specific section
        plugindata['bme_elev']  = self.bme_elev
        
    def saveform(self,plugindata):
        self._log.debug("Plugin: bme280 saveform")
        # generic section
        self._utils.plugin_saveform(self, plugindata)
        sf_i2c                  = plugindata.get('i2c',None)
        if sf_i2c: self.sf_i2c = int(sf_i2c)
        else: self.sf_i2c = None
        sf_i2c_addr                  = plugindata.get('i2c_addr',None)
        if sf_i2c_addr: self.i2c_addr = int(sf_i2c_addr)
        else: self.i2c_addr = None
        # plugin specific section
        sf_bme_elev                 = plugindata.get('bme_elev',None)
        if sf_bme_elev: self.bme_elev = int(sf_bme_elev)
        else: self.bme_elev = None
        self.i2c                    = core._hal.get_i2c(i2c)
        if self.i2c:
            try:
                if self.i2c != None: self.bme280_init(address=self.i2c_addr)
            except OSError as e:
                self._log.debug("Plugin: bme280 saveform OSError exception: "+repr(e))

    def read(self, values):
        self._log.debug("Plugin: bme280 read")
        # generic section
        values['valueN1'] = self.valuenames["valueN1"]
        values['valueN2'] = self.valuenames["valueN2"]
        values['valueN3'] = self.valuenames["valueN3"]
        
        #values['valueV1'] = round(self.valuenames["valueV1"], int(self.valuenames['valueD1']) ) # temp, specify decimal places
        #values['valueV2'] = round(self.valuenames["valueV2"], int(self.valuenames['valueD2']) ) # hum, specify decimal places
        #values['valueV3'] = round(self.valuenames["valueV3"], int(self.valuenames['valueD3']) ) # press, specify decimal places
        
        
        # plugin specific section
        if self.i2c != None: 
            try:
                dvalues = self.bme280_values()
                #print("bme280.py l.153: dvalues =", dvalues)
            except Exception as e:
                self._log.debug("Plugin: bme280 read exception: "+repr(e))
                values['valueV1'] = ''
                values['valueV2'] = ''
                values['valueV3'] = ''
                return values
            values["valueV1"] = round(float(dvalues[0]), int(self.valuenames['valueD1']) ) # temp, specify decimal places
            values["valueV2"] = round(float(dvalues[2]), int(self.valuenames['valueD2']) ) # hum, specify decimal places
            values["valueV3"] = round(float(dvalues[1]), int(self.valuenames['valueD3']) ) # press, specify decimal places
        else:
            self._log.debug("Plugin: BME280 read, empty values")
            # empty values
            values['valueV1'] = ''
            values['valueV2'] = ''
            values['valueV3'] = ''
        return values
   
    def write(self):
        self._log.debug("Plugin: bme280 write")

    async def asyncprocess(self):
        # plugin specific section
        self._log.debug("Plugin: bme280 process")
        if self.i2c: 
            try:
                t, p, h = self.bme280_read_compensated_data()
            except Exception as e:
                self._log.debug("Plugin: bme280 process exception: "+repr(e))
                # release lock, ready for next measurement
                self._lock.clear()
                return
            # send data to protocol and script/rule queues
            self.valuenames["valueV1"] = round(t/100, int(self.valuenames['valueD1']) ) # temp, specify decimal places
            self.valuenames["valueV2"] = round(h/1024, int(self.valuenames['valueD2']) ) # hum, specify decimal places
            self.valuenames["valueV3"] = round(p/25600, int(self.valuenames['valueD3']) ) # press, specify decimal places
            self._utils.plugin_senddata(self)
        # release lock, ready for next measurement
        self._lock.clear()
        
    #
    #CUSTOM SENSOR CODE...    
    #
    
    def bme280_init(self,
                 mode=BME280_OSAMPLE_1,
                 address=BME280_I2CADDR,
                 **kwargs):
        # Check that mode is valid.
        if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
                        BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
            raise ValueError(
                'Unexpected mode value {0}. Set mode to one of '
                'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
                'BME280_ULTRAHIGHRES'.format(mode))
        self._mode = mode
        self.address = address

        # load calibration data
        dig_88_a1 = self.i2c.readfrom_mem(self.address, 0x88, 26)
        dig_e1_e7 = self.i2c.readfrom_mem(self.address, 0xE1, 7)
        self.dig_T1, self.dig_T2, self.dig_T3, self.dig_P1, \
            self.dig_P2, self.dig_P3, self.dig_P4, self.dig_P5, \
            self.dig_P6, self.dig_P7, self.dig_P8, self.dig_P9, \
            _, self.dig_H1 = unpack("<HhhHhhhhhhhhBB", dig_88_a1)

        self.dig_H2, self.dig_H3 = unpack("<hB", dig_e1_e7)
        e4_sign = unpack_from("<b", dig_e1_e7, 3)[0]
        self.dig_H4 = (e4_sign << 4) | (dig_e1_e7[4] & 0xF)

        e6_sign = unpack_from("<b", dig_e1_e7, 5)[0]
        self.dig_H5 = (e6_sign << 4) | (dig_e1_e7[4] >> 4)

        self.dig_H6 = unpack_from("<b", dig_e1_e7, 6)[0]

        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
                             bytearray([0x3F]))
        self.t_fine = 0

        # temporary data holders which stay allocated
        self._l1_barray = bytearray(1)
        self._l8_barray = bytearray(8)
        self._l3_resultarray = array("i", [0, 0, 0])

    def bme280_read_raw_data(self, result):
        """ Reads the raw (uncompensated) data from the sensor.

            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order
            Returns:
                None
        """

        self._l1_barray[0] = self._mode
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL_HUM,
                             self._l1_barray)
        self._l1_barray[0] = self._mode << 5 | self._mode << 2 | 1
        self.i2c.writeto_mem(self.address, BME280_REGISTER_CONTROL,
                             self._l1_barray)

        sleep_time = 1250 + 2300 * (1 << self._mode)
        sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
        sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
        time.sleep_us(sleep_time)  # Wait the required time

        # burst readout from 0xF7 to 0xFE, recommended by datasheet
        self.i2c.readfrom_mem_into(self.address, 0xF7, self._l8_barray)
        readout = self._l8_barray
        # pressure(0xF7): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_press = ((readout[0] << 16) | (readout[1] << 8) | readout[2]) >> 4
        # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4
        raw_temp = ((readout[3] << 16) | (readout[4] << 8) | readout[5]) >> 4
        # humidity(0xFD): (msb << 8) | lsb
        raw_hum = (readout[6] << 8) | readout[7]

        result[0] = raw_temp
        result[1] = raw_press
        result[2] = raw_hum

    def bme280_read_compensated_data(self, result=None):
        """ Reads the data from the sensor and returns the compensated data.

            Args:
                result: array of length 3 or alike where the result will be
                stored, in temperature, pressure, humidity order. You may use
                this to read out the sensor without allocating heap memory

            Returns:
                array with temperature, pressure, humidity. Will be the one from
                the result parameter if not None
        """
        self.bme280_read_raw_data(self._l3_resultarray)
        raw_temp, raw_press, raw_hum = self._l3_resultarray
        # temperature
        var1 = ((raw_temp >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11)
        var2 = (((((raw_temp >> 4) - self.dig_T1) *
                  ((raw_temp >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14
        self.t_fine = var1 + var2
        temp = (self.t_fine * 5 + 128) >> 8

        # pressure
        var1 = self.t_fine - 128000
        var2 = var1 * var1 * self.dig_P6
        var2 = var2 + ((var1 * self.dig_P5) << 17)
        var2 = var2 + (self.dig_P4 << 35)
        var1 = (((var1 * var1 * self.dig_P3) >> 8) +
                ((var1 * self.dig_P2) << 12))
        var1 = (((1 << 47) + var1) * self.dig_P1) >> 33
        if var1 == 0:
            pressure = 0
        else:
            p = 1048576 - raw_press
            p = (((p << 31) - var2) * 3125) // var1
            var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
            var2 = (self.dig_P8 * p) >> 19
            pressure = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)

        # humidity
        h = self.t_fine - 76800
        h = (((((raw_hum << 14) - (self.dig_H4 << 20) -
                (self.dig_H5 * h)) + 16384)
              >> 15) * (((((((h * self.dig_H6) >> 10) *
                            (((h * self.dig_H3) >> 11) + 32768)) >> 10) +
                          2097152) * self.dig_H2 + 8192) >> 14))
        h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4)
        h = 0 if h < 0 else h
        h = 419430400 if h > 419430400 else h
        humidity = h >> 12

        if result:
            result[0] = temp
            result[1] = pressure
            result[2] = humidity
            return result

        return array("i", (temp, pressure, humidity))

    def bme280_values(self):
        """ human readable values """

        t, p, h = self.bme280_read_compensated_data()

        p = p // 256
        pi = p // 100
        pd = p - pi * 100

        hi = h // 1024
        hd = h * 100 // 1024 - hi * 100
        return ("{}".format(t / 100), "{}.{:02d}".format(pi, pd), # edited AJ, remove non-numerics
                "{}.{:02d}".format(hi, hd))
Esempio n. 19
0
 def __init__(self, uart):
     super().__init__(uart)
     self._nm_ack = Event()
     self._ps_ack = Event()
Esempio n. 20
0
class NEC_IR():
    def __init__(self, pin, callback, extended,
                 *args):  # Optional args for callback
        self._ev_start = Event()
        self._callback = callback
        self._extended = extended
        self._addr = 0
        self.block_time = 80 if extended else 73  # Allow for some tx tolerance (?)
        self._args = args
        self._times = array('i',
                            (0
                             for _ in range(_EDGECOUNT + 1)))  # +1 for overrun
        if platform == 'pyboard':
            ExtInt(pin, ExtInt.IRQ_RISING_FALLING, Pin.PULL_NONE, self._cb_pin)
        else:  # PR5962 ESP8266 hard IRQ's not supported
            pin.irq(handler=self._cb_pin,
                    trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING))
        #elif ESP32:
        #pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
        #else:
        #pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING), hard = True)
        self._edge = 0
        self._ev_start.clear()
        loop = asyncio.get_event_loop()
        loop.create_task(self._run())

    async def _run(self):
        loop = asyncio.get_event_loop()
        while True:
            await self._ev_start  # Wait until data collection has started
            # Compensate for asyncio latency
            latency = ticks_diff(loop.time(), self._ev_start.value())
            await asyncio.sleep_ms(self.block_time - latency
                                   )  # Data block should have ended
            self._decode()  # decode, clear event, prepare for new rx, call cb

    # Pin interrupt. Save time of each edge for later decode.
    def _cb_pin(self, line):
        t = ticks_us()
        # On overrun ignore pulses until software timer times out
        if self._edge <= _EDGECOUNT:  # Allow 1 extra pulse to record overrun
            if not self._ev_start.is_set():  # First edge received
                loop = asyncio.get_event_loop()
                self._ev_start.set(loop.time())  # asyncio latency compensation
            self._times[self._edge] = t
            self._edge += 1

    def _decode(self):
        overrun = self._edge > _EDGECOUNT
        val = OVERRUN if overrun else BADSTART
        if not overrun:
            width = ticks_diff(self._times[1], self._times[0])
            if width > 4000:  # 9ms leading mark for all valid data
                width = ticks_diff(self._times[2], self._times[1])
                if width > 3000:  # 4.5ms space for normal data
                    if self._edge < _EDGECOUNT:
                        # Haven't received the correct number of edges
                        val = BADBLOCK
                    else:
                        # Time spaces only (marks are always 562.5µs)
                        # Space is 1.6875ms (1) or 562.5µs (0)
                        # Skip last bit which is always 1
                        val = 0
                        for edge in range(3, _EDGECOUNT - 2, 2):
                            val >>= 1
                            if ticks_diff(self._times[edge + 1],
                                          self._times[edge]) > 1120:
                                val |= 0x80000000
                elif width > 1700:  # 2.5ms space for a repeat code. Should have exactly 4 edges.
                    val = REPEAT if self._edge == 4 else BADREP
        addr = 0
        if val >= 0:  # validate. Byte layout of val ~cmd cmd ~addr addr
            addr = val & 0xff
            cmd = (val >> 16) & 0xff
            if addr == ((val >> 8) ^ 0xff) & 0xff:  # 8 bit address OK
                val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA
                self._addr = addr
            else:
                addr |= val & 0xff00  # pass assumed 16 bit address to callback
                if self._extended:
                    val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA
                    self._addr = addr
                else:
                    val = BADADDR
        if val == REPEAT:
            addr = self._addr  # Last valid addresss
        self._edge = 0  # Set up for new data burst and run user callback
        self._ev_start.clear()
        self._callback(val, addr, *self._args)
Esempio n. 21
0
class test_plugin:
    gpiotype = "input"  # Default GPIO type
    inputtype = "normal"  # Default GPIO input type
    datastore = None  # Place where plugin data is stored for reboots

    def __init__(self):
        # generic section
        self._log = core._log
        self._log.debug("Plugin: test contruction")
        self._utils = core._utils
        self._plugins = core._plugins
        self._lock = Event()
        # plugin specific section
        self.valuenames = {}
        self.valuenames["valueN1"] = "GPIO"
        self.valuenames["valueF1"] = ""
        self.valuenames["valueD1"] = 0
        # release lock, ready for next measurement
        self._lock.clear()

    def init(self, plugin, device, queue, scriptqueue, rulequeue, valuequeue):
        self._log.debug("Plugin: test init")
        # generic section
        self._utils.plugin_initdata(self, plugin, device, queue, scriptqueue,
                                    rulequeue, valuequeue)
        self.content = plugin.get('content', content)
        self.pincnt = pincnt
        self.valuecnt = valuecnt
        self.stype = stype
        self.dtype = dtype
        self.valuenames['devicename'] = device[
            'name']  # gets device/plugin name, added AJ
        plugin['dtype'] = dtype
        plugin['stype'] = stype
        plugin['template'] = template
        datastore = self._plugins.readstore(name)
        return True

    def loadform(self, plugindata):
        self._log.debug("Plugin: test loadform")
        # generic section
        self._utils.plugin_loadform(self, plugindata)
        # plugin specific section
        plugindata['gpiotype'] = self.gpiotype
        plugindata['inputtype'] = self.inputtype

    def saveform(self, plugindata):
        self._log.debug("Plugin: test saveform")
        # generic section
        self._utils.plugin_saveform(self, plugindata)
        # plugin specific section
        self.gpiotype = plugindata['gpiotype']
        self.inputtype = plugindata['inputtype']
        self.dxpin = plugindata['dxpin0']

    def read(self, values):
        self._log.debug("Plugin: test read")
        # generic section
        values['valueN1'] = self.valuenames["valueN1"]
        # plugin specific section
        values['valueV1'] = 'on'

    def write(self, values):
        self._log.debug("Plugin: test write")

    async def asyncprocess(self):
        self._log.debug("Plugin: test process")
        # send data to protocol and script/rule queues
        self.valuenames["valueV1"] = 'on'
        self._utils.plugin_senddata(self)
        # release lock, ready for next measurement
        self._lock.clear()
Esempio n. 22
0
class ds18_plugin:
    valuenames          = {}
    datastore           = None
    roms                = None
    
    def __init__(self) :
        # generic section
        self._log       = core._log
        self._log.debug("Plugin: ds18 contruction")
        self._utils     = core._utils
        self._plugins   = core._plugins
        self._hal       = core._hal
        self._lock      = Event()
        # plugin specific section
        self.dxpin      = dxpin
        self.valuenames["valueN1"]= "Temperature"
        self.valuenames["valueF1"]= ""
        self.valuenames["valueD1"]= 0
        # release lock, ready for next measurement
        self._lock.clear()
        
    def init(self, plugin, device, queue, scriptqueue):        
        self._log.debug("Plugin: ds18 init")
        # generic section
        self._utils.plugin_initdata(self, plugin, device, queue, scriptqueue)
        self.pincnt             = pincnt
        self.valuecnt           = valuecnt
        self.stype              = stype
        self.content            = plugin.get('content',content)
        self.dxpin              = device.get('dxpin',dxpin)
        self._log.debug("Plugin: ds18 init dxpin"+self.dxpin)
        self.valuenames['devicename'] = device['name'] # gets device/plugin name, added AJ
        plugin['dtype']         = dtype
        plugin['stype']         = stype
        plugin['template']      = template
        datastore               = self._plugins.readstore(device["name"])
        # plugin specific section
        self._log.debug("Plugin: ds18 init, pin used: "+str(self.dxpin))
        # the device is on Dx
        self.mPin                   = self._hal.pin(self.dxpin)
        if self.mPin:
            # create the onewire object
            self.ds18                   = ds18x20.DS18X20(onewire.OneWire(self.mPin))
            # scan for devices on the bus
            roms                        = self.ds18.scan()
            # scan for devices on the bus
            self.roms                   = self.ds18.scan()

        return True

    def loadform(self,plugindata):
        self._log.debug("Plugin: ds18 loadform")
        # generic section
        self._utils.plugin_loadform(self, plugindata)
        # plugin specific section
        plugindata['resolution']= resolution
        romcnt = 0
        if not self.roms == None:
            for rom in self.roms:
                plugindata['rom'+str(romcnt)] = ''.join('{:02x}-'.format(x) for x in rom)[:-1]
                self._log.debug("Plugin: ds18 loadform, rom: "+plugindata['rom'+str(romcnt)])
                romcnt+=1
        else:
            self._log.debug("Plugin: ds18 loadform, no roms!")
        plugindata['romcnt']    = romcnt
        
    def saveform(self,plugindata):
        self._log.debug("Plugin: ds18 saveform")
        # generic section
        self._utils.plugin_saveform(self, plugindata)

        # plugin specific section
        self.romid                  = plugindata.get('deviceid','')
        self.resolution             = plugindata['resolution']

        # store values
        data = {}
        data["romid"]       = self.romid
        data["dxpin"]       = self.dxpin
        data["resolution"]  = self.resolution
        data["valueN1"]     = self.valuenames["valueN1"]
        data["valueF1"]     = self.valuenames["valueF1"] 
        data["valueD1"]     = self.valuenames["valueD1"]
        self._plugins.writestore(self.devicename, data)

        # the device is on Dx
        self.mPin                   = self._hal.pin(self.dxpin)
        self._log.debug("Plugin: ds18 saveform, pin used: "+str(self.dxpin))
        # create the onewire object
        self.ds18                   = ds18x20.DS18X20(onewire.OneWire(self.mPin))
        # scan for devices on the bus
        roms                        = self.ds18.scan()
        # scan for devices on the bus
        self.roms                   = self.ds18.scan()
        
    def read(self, values):
        self._log.debug("Plugin: ds18 read")
        # plugin specific section
        if not self.roms == None:
            for rom in self.roms:
                # Set convert on
                self.ds18.convert_temp()
                # wait 750ms for value to return
                #await asyncio.sleep_ms(750)
                import utime
                utime.sleep_ms(750)
                # Read temp
                values['valueN1'] = ''.join('{:02x}-'.format(x) for x in rom)[:-1]
                values["valueV1"] = round(self.ds18.read_temp(rom), int(self.valuenames['valueD1']) )
        else:
            self._log.debug("Plugin: ds18 read, empty values")
            # dummy values
            values['valueN1'] = ''
            values["valueV1"] = ''
   
    def write(self, values):
        self._log.debug("Plugin: ds18 write")

    async def asyncprocess(self):
        self._log.debug("Plugin: ds18 process")
        # plugin specific section
        if not self.roms == None:
            for rom in self.roms:
                # Set convert on
                self.ds18.convert_temp()
                # wait 750ms for value to return
                await asyncio.sleep_ms(750)
                # put temperature value(s) in queue
                try:
                    ds18temp = self.ds18.read_temp(rom)
                    self._log.debug("Plugin: ds18 data read: "+str(ds18temp))
                    # send data to protocol and script/rule queues
                    #self.valuenames["valueV1"] = ds18temp        
                    self.valuenames['valueV1'] = round(ds18temp, int(self.valuenames['valueD1']) )
                    self._utils.plugin_senddata(self)
                except Exception as e:
                    self._log.debug("Plugin: ds18 readtemp failed! Error: "+repr(e))
        # release lock, ready for next measurement
        self._lock.clear()
Esempio n. 23
0
class openhab_mqtt_protocol:
    processcnt = 1

    def __init__(self):
        self._log = core._log
        self._log.debug("Protocol: openhab mqtt contruction")
        self._lock = Event()
        # release lock, ready for next loop
        self._lock.clear()

    def init(self, protocol):
        self._log.debug("Protocol " + name + ": Init")
        self._client_id = protocol['client_id']
        self._server = protocol['hostname']
        self._port = protocol['port']
        self._user = protocol['user']
        self._password = protocol['password']
        self._queue_out1 = {}
        self._queue_out2 = {}
        self._queue_out3 = {}
        self._queue_out = protocol[
            'publish']  #### was commented out AJ, now back in
        self._pubstr = protocol['publish']  #### added AJ
        self._queue_in = protocol['subscribe']
        self._mq = MQTTClient(self._client_id, self._server, self._port,
                              self._user, self._password)
        # Print diagnostic messages when retries/reconnects happens
        self._mq.DEBUG = True
        self._queue = queues.Queue(maxsize=100)
        return self._queue

    def connect(self):
        self._log.debug("Protocol: " + name + ": connect")
        return self._mq.reconnect()

    def disconnect(self):
        self._log.debug("Protocol: " + name + ": disconnect")
        self._mq.disconnect()

    def check(self):
        self._log.debug("Protocol: " + name + ": check")
        self._mq.check_msg()

    def status(self):
        self._log.debug("Protocol: " + name + ": status")
        self._mq.ping()

    def recieve(self):
        self._log.debug("Protocol: " + name + ": recieve")
        self._mq.subscribe(self.queue_in)

    def send(self, devicedata):
        self._log.debug("Protocol: " + name + ": send " + devicedata["stype"])
        # connect or reconnect to mqtt server
        self.connect()
        mqttdata1 = None
        mqttdata2 = None
        mqttdata3 = None

        # case - all sensor types
        while True:
            mqttdata1 = None  # looks like duplication of above!
            mqttdata1 = {}
            mqttdata2 = {}
            mqttdata3 = {}
            self._queue_out1 = ''
            self._queue_out2 = ''
            self._queue_out3 = ''
            message1 = ''
            message2 = ''
            message3 = ''

            # Get next plugin datavalues from utils.py, plugin_senddata(self, queuedata)
            try:
                devicedata['unitname'] = self._queue.get_nowait()
                devicedata['devicename'] = self._queue.get_nowait()
            except Exception as e:
                self._log.debug("Protocol: " + name +
                                " SENSOR_TYPE_SINGLE exception: " + repr(e))
                break

            # case SENSOR_TYPE_SINGLE
            if devicedata["stype"] == core.SENSOR_TYPE_SINGLE:
                # get plugin values
                devicedata['valueV1'] = self._queue.get_nowait()
                devicedata['valueN1'] = self._queue.get_nowait()
                # Assemble mqtt message
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                break

            # case SENSOR_TYPE_LONG
            if devicedata["stype"] == core.SENSOR_TYPE_LONG:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_LONG")
                break

            # case SENSOR_TYPE_DUAL
            if devicedata["stype"] == core.SENSOR_TYPE_DUAL:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_DUAL")
                break

            # case SENSOR_TYPE_TEMP_HUM
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_TEMP_HUM")
                # Get plugin values
                try:
                    devicedata['valueV1'] = self._queue.get_nowait()
                    devicedata['valueN1'] = self._queue.get_nowait()
                    devicedata['valueV2'] = self._queue.get_nowait()
                    devicedata['valueN2'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol: " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM Exception: " +
                                    repr(e))
                    break

                # Assemble mqtt messages
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                mqttdata2 = {}
                mqttdata2['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN2']
                mqttdata2['msg'] = str(devicedata["valueV2"])
                message1 = str(devicedata["valueV2"])
                break

            # case SENSOR_TYPE_TEMP_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_BARO:
                self._log.debug("Protocol: " + name +
                                ": SENSOR_TYPE_TEMP_BARO")
                break

            # case SENSOR_TYPE_TEMP_HUM_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM_BARO:
                #self._log.debug("Protocol: "+name+": SENSOR_TYPE_TEMP_HUM_BARO")
                # Get plugin values
                try:
                    devicedata['valueV1'] = self._queue.get_nowait()
                    devicedata['valueN1'] = self._queue.get_nowait()
                    devicedata['valueV2'] = self._queue.get_nowait()
                    devicedata['valueN2'] = self._queue.get_nowait()
                    devicedata['valueV3'] = self._queue.get_nowait()
                    devicedata['valueN3'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol: " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM_BARO Exception: " +
                                    repr(e))
                    break
                # Assemble mqtt topics for valueV1, V2, V3
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                mqttdata2 = {}
                mqttdata2['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN2']
                mqttdata2['msg'] = str(devicedata["valueV2"])
                message2 = str(devicedata["valueV2"])
                mqttdata3 = {}
                mqttdata3['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN3']
                mqttdata3['msg'] = str(devicedata["valueV3"])
                message3 = str(devicedata["valueV3"])
                break

            # case SENSOR_TYPE_SWITCH
            if devicedata["stype"] == core.SENSOR_TYPE_SWITCH:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_SWITCH")
                # Get plugin values
                try:
                    devicedata['valueV1'] = self._queue.get_nowait()
                    devicedata['valueN1'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol: " + self._name +
                                    " SENSOR_TYPE_SWITCH Exception: " +
                                    repr(e))
                    break
                # Switches can have many values, OpenHAB (usually) only two: 1 (=on) or 0 (=off)
                switch_on = ['closed', 'press', 'double', 'long', 'on']
                switch_off = ['open', 'release', 'off']

                if devicedata["valueV1"] in switch_on:
                    devicedata["valueV1"] = 1
                elif devicedata["valueV1"] in switch_off:
                    devicedata["valueV1"] = 0
                else:
                    break

                # Assemble mqtt message
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                break

            # case SENSOR_TYPE_DIMMER
            if devicedata["stype"] == core.SENSOR_TYPE_DIMMER:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_DIMMER")
                break

            # case SENSOR_TYPE_WIND
            if devicedata["stype"] == core.SENSOR_TYPE_WIND:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_WIND")
                break

            # else UNKNOWN
            self._log.debug("Protocol " + name + ": Unknown sensor type!")
            break

        # Now publish the data to the MQTT broker/server

        # test for a user entry in webform protocol 'Publish' field; use it if it exists
        if self._pubstr != '':  # entry exists in Publish field
            self._queue_out1 = self._pubstr
            self._queue_out2 = self._pubstr
            self._queue_out3 = self._pubstr
        else:  # use "standard" format (unitname/devicename/valuename)...
            self._log.debug('Protocol: ' + name + ': "standard" topic format')
            self._queue_out1 = str(mqttdata1['topic'])
            if devicedata.get('valueN2') != None:
                self._queue_out2 = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN2']
            if devicedata.get('valueN3') != None:
                self._queue_out3 = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN3']

        # Whichever the sensor type, check if we have mqtt data to send, and if so publish it

        # publish datavalue1...
        if message1 != None:
            self._log.debug("Protocol: " + name + " Publish: Topic: " +
                            self._queue_out1 + ", Message: " + message1)
            self._mq.publish(self._queue_out1, message1)

        # publish datavalue2 (if it exists)
        if devicedata.get('valueN2') != None:
            if message2 != None:
                self._log.debug("Protocol: " + name + " Publish: Topic: " +
                                self._queue_out2 + ", Message: " + message2)
                self._mq.publish(self._queue_out2, message2)

        # publish datavalue3 (if it exists)
        if devicedata.get('valueN3') != None:
            if mqttdata3['msg'] != None:
                self._log.debug("Protocol: " + name + " Publish: Topic: " +
                                self._queue_out3 + ", Message: " + message3)
                self._mq.publish(self._queue_out3, message3)

        # we may eventually need more, for example for Dummy device (4 values)...
        # End of send #

    def process(self):
        # processing todo for protocol (main loop of protocol)
        self._log.debug("Protocol: " + name + " Processing...")
        devicedata = {}
        try:
            while True:
                message1 = self._queue.get_nowait(
                )  # keep reading from the protocol queue
                if message1 == core.QUEUE_MESSAGE_START:  # found "start" message
                    break  # ready to read devicedata values
            devicedata['stype'] = self._queue.get_nowait()  # get sensor type
            devicedata['serverid'] = self._queue.get_nowait()  # get server id
            #print("OHmqtt l 266: devicedata = ", devicedata)
            self.send(
                devicedata
            )  # go and get other datavalues as needed, and publish them to MQTT ...
        except Exception as e:
            self._log.debug("Protocol: " + name + " process Exception: " +
                            repr(e))

        # release lock, ready for next processing
        self._lock.clear()
Esempio n. 24
0
class domoticz_mqtt_protocol:
    processcnt = 1

    def __init__(self):
        self._log = core._log
        self._log.debug("Protocol: domoticz mqtt contruction")
        self._lock = Event()
        # release lock, ready for next loop
        self._lock.clear()

    def init(self, protocol):
        self._log.debug("Protocol " + name + ": Init")
        self._client_id = protocol['client_id']
        self._server = protocol['hostname']
        self._port = protocol['port']
        self._user = protocol['user']
        self._password = protocol['password']
        self._queue_out = protocol['publish']
        self._queue_in = protocol['subscribe']
        self._mq = MQTTClient(self._client_id, self._server, self._port,
                              self._user, self._password)
        # Print diagnostic messages when retries/reconnects happens
        #self._mq.DEBUG = True
        self._queue = queues.Queue(maxsize=100)
        return self._queue

    def connect(self):
        self._log.debug("Protocol " + name + ": connect")
        return self._mq.reconnect()

    def disconnect(self):
        self._log.debug("Protocol " + name + ": disconnect")
        self._mq.disconnect()

    def check(self):
        self._log.debug("Protocol " + name + ": check")
        self._mq.check_msg()

    def status(self):
        self._log.debug("Protocol " + name + ": status")
        self._mq.ping()

    def recieve(self):
        self._log.debug("Protocol " + name + ": recieve")
        self._mq.subscribe(self.queue_in)

    def send(self, devicedata):
        self._log.debug("Protocol " + name + ": send " + devicedata["stype"])
        # connect or reconnect to mqtt server
        self.connect()
        mqttdata = None
        # case
        while True:
            mqttdata = None

            # case SENSOR_TYPE_SINGLE
            if devicedata["stype"] == core.SENSOR_TYPE_SINGLE:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_SINGLE")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + name +
                                    " SENSOR_TYPE_SINGLE exception: " +
                                    repr(e))
                    break

                # Assemble mqtt message
                mqttdata = {}
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["nvalue"] = 0
                mqttdata["svalue"] = str(devicedata["value1"])
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_LONG
            if devicedata["stype"] == core.SENSOR_TYPE_LONG:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_LONG")
                break

            # case SENSOR_TYPE_DUAL
            if devicedata["stype"] == core.SENSOR_TYPE_DUAL:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_DUAL")
                break

            # case SENSOR_TYPE_TEMP_HUM
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_TEMP_HUM")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                    devicedata['value2'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM Exception: " +
                                    repr(e))
                    break
                # Assemble mqtt message
                mqttdata = {}
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["nvalue"] = 0
                mqttdata["svalue"] = str(devicedata["value1"]) + ";" + str(
                    devicedata["value2"]) + ";0"
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_TEMP_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_BARO:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_TEMP_BARO")
                break

            # case SENSOR_TYPE_TEMP_HUM_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM_BARO:
                self._log.debug("Protocol " + name +
                                ": SENSOR_TYPE_TEMP_HUM_BARO")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                    devicedata['value2'] = self._queue.get_nowait()
                    devicedata['value3'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM_BARO Exception: " +
                                    repr(e))
                    break
                # Assemble mqtt message
                mqttdata = {}
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["nvalue"] = 0
                mqttdata["svalue"] = str(devicedata["value1"]) + ";" + str(
                    devicedata["value2"]) + ";0;" + str(
                        devicedata["value3"]) + ";0"
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_SWITCH
            if devicedata["stype"] == core.SENSOR_TYPE_SWITCH:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_SWITCH")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + self._name +
                                    " SENSOR_TYPE_SWITCH Exception: " +
                                    repr(e))
                    break

                # Switches can have many values, domoticz only two: on or off
                switch_on = ['closed', 'press', 'double', 'long']
                switch_off = ['open', 'release']

                if devicedata["value1"] in switch_on:
                    devicedata["value1"] = 'On'
                elif devicedata["value1"] in switch_off:
                    devicedata["value1"] = 'Off'
                else:
                    break

                # Assemble mqtt message
                mqttdata = {}
                mqttdata["command"] = "switchlight"
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["switchcmd"] = devicedata["value1"]
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_DIMMER
            if devicedata["stype"] == core.SENSOR_TYPE_DIMMER:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_DIMMER")
                break

            # case SENSOR_TYPE_WIND
            if devicedata["stype"] == core.SENSOR_TYPE_WIND:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_WIND")
                break

            # else UNKNOWN
            self._log.debug("Protocol " + name + ": Unknown sensor type!")
            break

        if mqttdata != None:
            self._log.debug("Protocol " + name + ": Message: " + message)
            self._mq.publish(self._queue_out, message)

    def process(self):
        # processing todo for protocol
        self._log.debug("Protocol " + name + " Processing...")
        devicedata = {}
        try:
            while True:
                message = self._queue.get_nowait()
                if message == core.QUEUE_MESSAGE_START:
                    break
            devicedata['stype'] = self._queue.get_nowait()
            devicedata['serverid'] = self._queue.get_nowait()
            self.send(devicedata)
        except Exception as e:
            self._log.debug("Protocol " + name + " process Exception: " +
                            repr(e))

        # release lock, ready for next processing
        self._lock.clear()
Esempio n. 25
0
class domoticz_http_protocol:
    processcnt = 1

    def __init__(self):
        self._log = core._log
        self._log.debug("Protocol: domoticz http contruction")
        self._lock = Event()
        # release lock, ready for next loop
        self._lock.clear()

    def init(self, protocol):
        self._log.debug("Protocol " + name + ": Init")
        self._client_id = protocol['client_id']
        self._server = protocol['hostname']
        self._port = protocol['port']
        self._user = protocol['user']
        self._password = protocol['password']
        self._queue = queues.Queue(maxsize=100)
        return self._queue

    def connect(self):
        self._log.debug("Protocol " + name + ": connect")

    def disconnect(self):
        self._log.debug("Protocol " + name + ": disconnect")

    def check(self):
        self._log.debug("Protocol " + name + ": check")

    def status(self):
        self._log.debug("Protocol " + name + ": status")

    def receive(self):
        self._log.debug("Protocol " + name + ": recieve")

    def send(self, devicedata):
        self._log.debug("Protocol " + name + ": send " + devicedata["stype"])
        # Assemble server url
        message = None
        # case
        while True:

            # case SENSOR_TYPE_SINGLE
            if devicedata["stype"] == core.SENSOR_TYPE_SINGLE:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_SINGLE")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                except Exception:
                    self._log.debug(
                        "Protocol " + name +
                        " SENSOR_TYPE_TEMP_HUM exception: Queue Emtpy!")
                    break

                # Assemble http message
                message = "/json.htm?type=command&param=udevice&idx=" + str(
                    devicedata["serverid"]) + "&nvalue=0&svalue=" + str(
                        devicedata["value1"]) + ";0"
                break

            # case SENSOR_TYPE_LONG
            if devicedata["stype"] == core.SENSOR_TYPE_LONG:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_LONG")
                break

            # case SENSOR_TYPE_DUAL
            if devicedata["stype"] == core.SENSOR_TYPE_DUAL:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_DUAL")
                break

            # case SENSOR_TYPE_TEMP_HUM
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_TEMP_HUM")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                    devicedata['value2'] = self._queue.get_nowait()
                except Exception:
                    self._log.debug(
                        "Protocol " + name +
                        " SENSOR_TYPE_TEMP_HUM exception: Queue Emtpy!")
                    break
                # Assemble http message
                message = "/json.htm?type=command&param=udevice&idx=" + str(
                    devicedata["serverid"]) + "&nvalue=0&svalue=" + str(
                        devicedata["value1"]) + str(
                            devicedata["value2"]) + ";0"
                break

            # case SENSOR_TYPE_TEMP_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_BARO:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_TEMP_BARO")
                break

            # case SENSOR_TYPE_TEMP_HUM_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM_BARO:
                self._log.debug("Protocol " + name +
                                ": SENSOR_TYPE_TEMP_HUM_BARO")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                    devicedata['value2'] = self._queue.get_nowait()
                    devicedata['value3'] = self._queue.get_nowait()
                except Exception:
                    self._log.debug(
                        "Protocol " + name +
                        " SENSOR_TYPE_TEMP_HUM_BARO exception: Queue Emtpy!")
                    break
                # Assemble http message
                message = "/json.htm?type=command&param=udevice&idx=" + str(
                    devicedata["serverid"]) + "&nvalue=0&svalue=" + str(
                        devicedata["value1"]) + str(
                            devicedata["value2"]) + ";0;" + str(
                                devicedata["value3"]) + ";0"
                break

            # case SENSOR_TYPE_SWITCH
            if devicedata["stype"] == core.SENSOR_TYPE_SWITCH:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_SWITCH")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                except Exception:
                    self._log.debug(
                        "Protocol " + name +
                        " SENSOR_TYPE_SWITCH exception: Queue Emtpy!")
                    break

                # Switches can have many values, domoticz only two: on or off
                switch_on = ['closed', 'press', 'double', 'long']
                switch_off = ['open', 'release']

                if devicedata["value1"] in switch_on:
                    devicedata["value1"] = 'On'
                elif devicedata["value1"] in switch_off:
                    devicedata["value1"] = 'Off'
                else:
                    break

                # Assemble http message
                message = "/json.htm?type=command&param=switchlight&idx=" + str(
                    devicedata["serverid"]
                ) + "&switchcmd=" + devicedata["value1"]
                break

            # case SENSOR_TYPE_DIMMER
            if devicedata["stype"] == core.SENSOR_TYPE_DIMMER:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_DIMMER")
                break

            # case SENSOR_TYPE_WIND
            if devicedata["stype"] == core.SENSOR_TYPE_WIND:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_WIND")
                break

            # else UNKNOWN
            self._log.debug("Protocol " + name + ": Unknown sensor type!")
            break

        if message != None:
            self._log.debug("Protocol " + name + " message: " + "http://" +
                            self._server + ":" + str(self._port) + message)
            # Send data
            try:
                response = urequests.get("http://" + self._server + ":" +
                                         str(self._port) + message)
                self._log.debug("Protocol " + name + " response: " +
                                response.text)
                response.close()
            except OSError as e:
                self._log.debug("Protocol " + name + " Exception: " + repr(e))
            #self._log.debug("Protocol "+name+" response: "+resp.read().decode("utf-8"))

    def process(self):
        # processing todo for protocol
        self._log.debug("Protocol " + name)
        devicedata = {}
        try:
            while self._queue.get_nowait() != core.QUEUE_MESSAGE_START:
                pass
            devicedata['stype'] = self._queue.get_nowait()
            devicedata['serverid'] = self._queue.get_nowait()
        except Exception as e:
            self._log.debug("Protocol " + name + " proces Exception: " +
                            repr(e))
        self.send(devicedata)

        # release lock, ready for next processing
        self._lock.clear()
Esempio n. 26
0
import uasyncio

from sys import stdin, stdout
from io import BytesIO
from binascii import unhexlify, hexlify

from asyn import Event
from m5stack import LCD, fonts, color565, SDCard, buttons, keyboard, qr
from bitcoin.mnemonic import secure_mnemonic, WORD_LIST
from bitcoin.hd import HDPrivateKey
from bitcoin.tx import Tx
from bitcoin.script import Script

# globals
lcd = LCD()
SIGN_IT = Event()
DONT_SIGN_IT = Event()
SIGNED = Event()
KEY = None
PARTIAL_QR = ''
last_qr = 0

KEYBOARD = keyboard.KeyboardDriver(cb_fall=lambda value: lcd.print(str(value)))


async def qr_callback(b):
    # this is very hacky b/c qr driver will return parts of a qr code at-a-time
    # and i don't have a standard way to know that I have the whole thing (need checksum or something)
    # also, doesn't accept commands yet ... just transaction signing ...
    global PARTIAL_QR, last_qr
    if time.time() - last_qr > 1:
Esempio n. 27
0
class ssd1306_plugin:
    datastore           = None                      # Place where plugin data is stored for reboots
    
    def __init__(self) :
        # generic section
        self._log       = core._log
        self._log.debug("Plugin: ssd1306 contruction")
        self._utils     = core._utils
        self._plugins   = core._plugins
        self._lock      = Event()
        # plugin specific section
        self.valuenames = {}
        self.valuenames["valueN1"]= ""
        self.valuenames["valueF1"]= ""
        self.valuenames["valueD1"]= 0
        # release lock, ready for next measurement
        self._lock.clear()
        
    def init(self, plugin, device, queue, scriptqueue):        
        self._log.debug("Plugin: ssd1306 init")
        # generic section
        self._utils.plugin_initdata(self, plugin, device, queue, scriptqueue)
        self.content            = plugin.get('content',content)
        self.pincnt             = pincnt
        self.valuecnt           = valuecnt
        self.stype              = stype
        self.dtype              = dtype
        self.ssd_i2c            = ssd_i2c
        self.ssd_rotation       = ssd_rotation
        self.ssd_width          = ssd_width
        self.ssd_height         = ssd_height
        self.ssd_timeout        = ssd_timeout
        self.ssd_line1          = ssd_line1
        self.ssd_line2          = ssd_line2
        self.ssd_line3          = ssd_line3
        self.ssd_line4          = ssd_line4
        self.ssd_line5          = ssd_line5
        self.ssd_line6          = ssd_line6
        self.ssd_line7          = ssd_line7
        self.ssd_line8          = ssd_line8
        self._device            = device
        plugin['dtype']         = dtype
        plugin['stype']         = stype
        plugin['template']      = template
        datastore               = self._plugins.readstore(name)
        # Load values
        self._plugins.loadvalues(self._device,self.valuenames)
        
        # Set triggers
        self.triggers           = name+"#"+self.valuenames["valueN1"]
        self._plugins.triggers(device, self.triggers)
        return True

    def loadform(self,plugindata):
        self._log.debug("Plugin: ssd1306 loadform")
        # generic section
        self._utils.plugin_loadform(self, plugindata)
        # plugin specific section
        plugindata["ssd_i2c"]      = self.ssd_i2c
        plugindata["ssd_rotation"] = self.ssd_rotation
        plugindata["ssd_height"]   = self.ssd_height
        plugindata["ssd_width"]    = self.ssd_width 
        plugindata["ssd_timeout"]  = self.ssd_timeout
        
    def saveform(self,plugindata):
        self._log.debug("Plugin: ssd1306 saveform")
        # generic section
        self._utils.plugin_saveform(self, plugindata)
        # plugin specific section
        self._plugins.savevalues(self._device,self.valuenames)
        
    def read(self, values):
        self._log.debug("Plugin: ssd1306 read")
        # generic section
        values['valueN1'] = self.valuenames["valueN1"]
        # plugin specific section
        values['valueV1'] = 'on'

    def write(self, values):
        self._log.debug("Plugin: ssd1306 write")
        #print(values)
        
    async def asyncprocess(self):
        self._log.debug("Plugin: ssd1306 process")
        # send data to protocol and script/rule queues
        self.valuenames["valueV1"] = 'on'        
        self._utils.plugin_senddata(self)
        # release lock, ready for next measurement
        self._lock.clear()
Esempio n. 28
0
class SyncedClock_RTC(syncedclock.SyncedClock):

    # since pyb.RTC() is unreliable for reads, do it ourselves directly
    _RTC_BASE = const(0x40002800)
    _RTC_SSR_OFFSET = const(0x28)
    _rtc_ssr_struct = {
        "ss": 0 | uctypes.BFUINT32 | 0 << uctypes.BF_POS | 15 << uctypes.BF_LEN
    }
    _RTC_TR_OFFSET = const(0x00)
    _rtc_tr_struct = {
        "pm":
        0 | uctypes.BFUINT32 | 22 << uctypes.BF_POS | 1 << uctypes.BF_LEN,
        "ht":
        0 | uctypes.BFUINT32 | 20 << uctypes.BF_POS | 2 << uctypes.BF_LEN,
        "hu":
        0 | uctypes.BFUINT32 | 16 << uctypes.BF_POS | 4 << uctypes.BF_LEN,
        "mnt":
        0 | uctypes.BFUINT32 | 12 << uctypes.BF_POS | 3 << uctypes.BF_LEN,
        "mnu":
        0 | uctypes.BFUINT32 | 8 << uctypes.BF_POS | 4 << uctypes.BF_LEN,
        "st": 0 | uctypes.BFUINT32 | 4 << uctypes.BF_POS | 3 << uctypes.BF_LEN,
        "su": 0 | uctypes.BFUINT32 | 0 << uctypes.BF_POS | 4 << uctypes.BF_LEN
    }
    _RTC_DR_OFFSET = const(0x04)
    _rtc_dr_struct = {
        "yt":
        0 | uctypes.BFUINT32 | 20 << uctypes.BF_POS | 4 << uctypes.BF_LEN,
        "yu":
        0 | uctypes.BFUINT32 | 16 << uctypes.BF_POS | 4 << uctypes.BF_LEN,
        "wdu":
        0 | uctypes.BFUINT32 | 13 << uctypes.BF_POS | 3 << uctypes.BF_LEN,
        "mt":
        0 | uctypes.BFUINT32 | 12 << uctypes.BF_POS | 1 << uctypes.BF_LEN,
        "mu": 0 | uctypes.BFUINT32 | 8 << uctypes.BF_POS | 4 << uctypes.BF_LEN,
        "dt": 0 | uctypes.BFUINT32 | 4 << uctypes.BF_POS | 2 << uctypes.BF_LEN,
        "du": 0 | uctypes.BFUINT32 | 0 << uctypes.BF_POS | 4 << uctypes.BF_LEN
    }

    _RTC_MAX = const(8191)

    # wrap initialiser
    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self._uart = None
        self._pps_pin = None
        if kwargs is not None:
            if 'gps_uart' in kwargs:
                self._uart = kwargs['gps_uart']
            if 'pps_pin' in kwargs:
                self._pps_pin = kwargs['pps_pin']

        if (self._uart == None):
            raise ValueError("need a uart for the gps")
        if (self._pps_pin == None):
            raise ValueError("need a pin that gps sends 1pps to us on")

        # we also need the RTC device
        self._rtc = RTC()
        self._pps_event = Event()
        self._rtc_ssr = uctypes.struct(_RTC_BASE + _RTC_SSR_OFFSET,
                                       self._rtc_ssr_struct, uctypes.NATIVE)
        self._rtc_dr = uctypes.struct(_RTC_BASE + _RTC_DR_OFFSET,
                                      self._rtc_dr_struct, uctypes.NATIVE)
        self._pps_rtc = 0
        self._pps_discard = 0
        self._ss_offset = -10000
        self._refclk = (0, 0, 0, 0, 0, 0)

    # try to get some better perf out of this, it's staticish code
    @micropython.native
    def _pps(self, p):
        # grab RTC data when we tick
        # we need to pull this directly out of the registers because we don't want to
        # allocate ram, and the RTC() module does
        self._pps_rtc = self._rtc_ssr.ss
        # need to read DR to nothing to unlock shadow registers
        self._pps_discard = self._rtc_dr.du
        self._pps_event.set()
        return

    async def _wait_gpslock(self):
        try:
            while True:
                if (self._gps.isLocked()):
                    return True
                await asyncio.sleep(1)
        except asyncio.TimeoutError:
            print("syncedclock_rtc: failed to get lock, reinit gps")
        return False

    async def _wait_pps(self):
        try:
            await self._pps_event
            self._pps_event.clear()
            return True
        except asyncio.TimeoutError:
            print("syncedclock_rtc: failed to get pps event in time")
        return False

    # this will now be running in a thread, safe to do things which block
    async def _calibration_loop(self):
        # start RTC
        print("syncedclock_rtc: start rtc")
        self._rtc.init()
        # initalise gps
        self._gps = GPS(self._uart)
        ppsint = ExtInt(self._pps_pin, ExtInt.IRQ_RISING, Pin.PULL_NONE,
                        self._pps)
        ppsint.disable()
        self._pps_event.clear()
        await asyncio.sleep(0)
        while True:
            print("syncedclock_rtc: initalise gps")
            await self._gps.set_auto_messages(['RMC'], 1)
            await self._gps.set_pps_mode(GPS.PPS_Mode.FIX, 42,
                                         GPS.PPS_Polarity.ACTIVE_HIGH, 0)
            print("syncedclock_rtc: waiting for gps lock (30s)")
            res = await asyncio.wait_for(self._wait_gpslock(), 30)
            if (res == False):
                continue
            print(
                "syncedclock_rtc: gps locked, start pps interrupt and wait for pps (3s)"
            )
            #self._pps_pin.irq(trigger=Pin.IRQ_RISING, handler=self._pps)
            ppsint.enable()
            res = await asyncio.wait_for(self._wait_pps(), 3)
            if (res == False):
                print(
                    "syncedclock_rtc: pps signal never recieved, bad wiring?")
                print("syncedclock_rtc: terminating")
                return
            # PPS signal leads GPS data by about half a second or so
            # so the GPS data contains the *previous* second at this point
            # add 1 second and reset RTC
            print("syncedclock_rtc: pps pulse recieved, set RTC clock")
            date = self._gps.date()
            time = self._gps.time()
            # helpfully utime and pyb.RTC use different order in the tuple
            now = utime.localtime(
                utime.mktime((date[2], date[1], date[0], time[0], time[1],
                              time[2], 0, 0)) + 1)
            self._rtc.datetime(
                (now[0], now[1], now[2], 0, now[3], now[4], now[5], 0))
            print("syncedclock_rtc: rtc clock now", self._rtc.datetime())
            await asyncio.sleep(0)
            print("syncedclock_rtc: calibration loop started")
            # ensure we ignore the first cycle
            last_rtc_ss = -1
            # count 32 seconds and calculate the error
            count = 0
            tick_error = 0
            while True:
                # each time we get an PPS event, work out the ticks difference
                res = await asyncio.wait_for(self._wait_pps(), 3)
                if (res == False):
                    print("syncedclock_rtc: lost pps signal, restarting")
                    self._locked = False
                    #self._pps_pin.irq(handler=None)
                    ppsint.disable()
                    break
                rtc_ss = self._pps_rtc
                await asyncio.sleep(0)
                # first pass, just discard the value
                if (last_rtc_ss == -1):
                    last_rtc_ss = rtc_ss
                    continue
                await asyncio.sleep(0)
                count += 1
                # compute the difference in ticks between this and the last
                error = (rtc_ss - last_rtc_ss)
                if (abs(error) > _RTC_MAX - 50):
                    # probably actually rollover problem
                    if (rtc_ss < last_rtc_ss):
                        error = (rtc_ss + _RTC_MAX + 1 - last_rtc_ss)
                    else:
                        error = (rtc_ss - last_rtc_ss + _RTC_MAX + 1)
                await asyncio.sleep(0)
                #print(error)
                tick_error += (error)
                last_rtc_ss = rtc_ss
                # always use the last top of second as current offset if it's not a huge error
                # when locked
                if (self._locked and tick_error > -1 and tick_error < 1):
                    self._ss_offset = rtc_ss
                await asyncio.sleep(0)
                if (count == 32):
                    # if the tick error is +/-1 ticks, it's locked enough
                    # we're only then about 3.81ppm but that's close enough
                    if (self._locked == True
                            and (tick_error > 1 or tick_error < -1)):
                        print("syncedclock_rtc: lost lock")
                        self._locked = False
                    await asyncio.sleep(0)
                    if (self._locked == False
                            and (tick_error <= 1 and tick_error >= -1)):
                        print("syncedclock_rtc: locked with",
                              self._rtc.calibration())
                        # only cache top of second when we enter lock
                        #self._ss_offset = (_RTC_MAX-rtc_ss)
                        self._locked = True
                    await asyncio.sleep(0)
                    if (self._locked == True):
                        # update reference clock point
                        self._refclk = self._rtc.datetime()
                    await asyncio.sleep(0)
                    if (self._locked == False):
                        print("syncedclock_rtc: error now", tick_error)
                    # the total ticks missing should be applied to the calibration
                    # we do this continously so we can ensure the clock is always remaining in sync
                    await asyncio.sleep(0)
                    try:
                        self._rtc.calibration(self._rtc.calibration() +
                                              tick_error)
                    except:
                        print("syncedclock_rtc: error too large, ignoring")
                    # allow us to to be interrupted now
                    await asyncio.sleep(0)
                    #print(rtc.calibration())
                    count = 0
                    tick_error = 0

    def _rtc_to_unixtime(self, rtc_tuple, rtc_offset):
        ts = utime.mktime((
            rtc_tuple[0],  # year
            rtc_tuple[1],  # month
            rtc_tuple[2],  # day
            rtc_tuple[4],  # hour
            rtc_tuple[5],  # minute
            rtc_tuple[6],  # second
            0,
            0)) + 946684800  # weekday and dayofyear are ignored
        tss = (_RTC_MAX - rtc_tuple[7]) + rtc_offset
        if tss >= _RTC_MAX:
            tss -= _RTC_MAX + 1
            ts += 1
        if tss < 0:
            tss += _RTC_MAX + 1
            ts -= 1
        return (ts, tss << 19)

    def now(self):
        if not self._locked:
            return None
        return self._rtc_to_unixtime(self._rtc.datetime(), self._ss_offset)

    def refclk(self):
        if not self._locked:
            return None
        return self._rtc_to_unixtime(self._refclk, self._ss_offset)

    async def start(self):
        super().start()
        loop = asyncio.get_event_loop()
        loop.create_task(self._calibration_loop())
        print("syncedclock_rtc: calibration loop created")
        await asyncio.sleep(0)
        return