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)
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()
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)
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()
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()
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()
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)
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¶m=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¶m=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¶m=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¶m=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()
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()
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()
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))
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
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()
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