<< return to Vizycam.com

User Tools

Site Tools


wiki:test

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
wiki:test [2021/02/04 16:53]
vizycam
wiki:test [2021/10/16 18:30] (current)
vizycam
Line 1: Line 1:
-====== Module vizypowerboard ======+[[wiki:​running_utilities|utilities]]
  
-Module docstring+[[wiki:​pinoouts|pinouts]]
  
-===== Variables =====+[[wiki:​ssh|ssh]]
  
-  * **''​%%DIPSWITCH_EXT_BUTTON%%''​** docstring for ext_button+[[wiki:​birdfeeder_app|birdfeeder]]
  
-===== Classes =====+[[wiki:​stage|stage]]
  
-<​HTML><​dl></HTML+hello 
-<HTML><dt></HTML>''​%%VizyPowerBoard(addr=20bus=1)%%''​<HTML></dt></HTML+<​HTML>​ 
-<HTML><dd></HTML><HTML><p></HTML>This the docstring for VizyPowerBoard<HTML></​p></​HTML+<!doctype html> 
-==== Static methods ====+<html lang="​en">​ 
 +<​head>​ 
 +<meta charset="​utf-8">​ 
 +<meta name="​viewport"​ content="​width=device-width,​ initial-scale=1,​ minimum-scale=1" ​/> 
 +<meta name="​generator"​ content="​pdoc 0.9.2" /> 
 +<title>vizypowerboard API documentation</title> 
 +<meta name="​description"​ content="​This module is used with the Vizy Power Board, which is a printed circuit 
 +board that plugs into the Raspberry Pi I/O connector and is included 
 +with …" /> 
 +<link rel="​preload stylesheet"​ as="​style"​ href="​https://​cdnjs.cloudflare.com/​ajax/​libs/​10up-sanitize.css/​11.0.1/​sanitize.min.css"​ integrity="​sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs="​ crossorigin>​ 
 +<link rel="​preload stylesheet"​ as="​style"​ href="​https://​cdnjs.cloudflare.com/​ajax/​libs/​10up-sanitize.css/​11.0.1/​typography.min.css"​ integrity="​sha256-7l/​o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg="​ crossorigin>​ 
 +<link rel="​stylesheet preload"​ as="​style"​ href="​https://​cdnjs.cloudflare.com/​ajax/​libs/​highlight.js/​10.1.1/​styles/​github.min.css"​ crossorigin>​ 
 +<​style>:​root{--highlight-color:#​fe9}.flex{display:​flex !important}body{line-height:​1.5em}#​content{padding:​10px}#​sidebar{padding:​15px;​overflow:​hidden}#​sidebar > *:​last-child{margin-bottom:​2cm}.http-server-breadcrumbs{font-size:​130%;margin:0 0 15px 0}#​footer{font-size:​.75em;​padding:​5px 30px;​border-top:​1px solid #​ddd;​text-align:​right}#​footer p{margin:0 0 0 1em;​display:​inline-block}#​footer p:​last-child{margin-right:​30px}h1,​h2,​h3,​h4,​h5{font-weight:​300}h1{font-size:​2.5em;​line-height:​1.1em}h2{font-size:​1.75em;​margin:​1em 0 .50em 0}h3{font-size:​1.4em;​margin:​25px 0 10px 0}h4{margin:​0;​font-size:​105%}h1:​target,​h2:​target,​h3:​target,​h4:​target,​h5:​target,​h6:​target{background:​var(--highlight-color);​padding:​.2em 0}a{color:#​058;​text-decoration:​none;​transition:​color .3s ease-in-out}a:​hover{color:#​e82}.title code{font-weight:​bold}h2[id^="​header-"​]{margin-top:​2em}.ident{color:#​900}pre code{background:#​f8f8f8;​font-size:​.8em;​line-height:​1.4em}code{background:#​f2f2f1;​padding:​1px 4px;​overflow-wrap:​break-word}h1 code{background:​transparent}pre{background:#​f8f8f8;​border:​0;​border-top:​1px solid #​ccc;​border-bottom:​1px solid #​ccc;​margin:​1em 0;​padding:​1ex}#​http-server-module-list{display:​flex;​flex-flow:​column}#​http-server-module-list div{display:​flex}#​http-server-module-list dt{min-width:​10%}#​http-server-module-list p{margin-top:​0}.toc ul,#​index{list-style-type:​none;​margin:​0;​padding:​0}#​index code{background:​transparent}#​index h3{border-bottom:​1px solid #ddd}#index ul{padding:​0}#​index h4{margin-top:​.6em;​font-weight:​bold}@media (min-width:​200ex){#​index .two-column{column-count:​2}}@media (min-width:​300ex){#​index .two-column{column-count:​3}}dl{margin-bottom:​2em}dl dl:​last-child{margin-bottom:​4em}dd{margin:​0 0 1em 3em}#​header-classes + dl > dd{margin-bottom:​3em}dd dd{margin-left:​2em}dd p{margin:​10px 0}.name{background:#​eee;​font-weight:​bold;​font-size:​.85em;​padding:​5px 10px;​display:​inline-block;​min-width:​40%}.name:​hover{background:#​e0e0e0}dt:​target .name{background:​var(--highlight-color)}.name > span:​first-child{white-space:​nowrap}.name.class > span:​nth-child(2){margin-left:​.4em}.inherited{color:#​999;​border-left:​5px solid #​eee;​padding-left:​1em}.inheritance em{font-style:​normal;​font-weight:​bold}.desc h2{font-weight:​400;​font-size:​1.25em}.desc h3{font-size:​1em}.desc dt code{background:​inherit}.source summary,​.git-link-div{color:#​666;​text-align:​right;​font-weight:​400;​font-size:​.8em;​text-transform:​uppercase}.source summary > *{white-space:​nowrap;​cursor:​pointer}.git-link{color:​inherit;​margin-left:​1em}.source pre{max-height:​500px;​overflow:​auto;​margin:​0}.source pre code{font-size:​12px;​overflow:​visible}.hlist{list-style:​none}.hlist li{display:​inline}.hlist li:​after{content:',​\2002'​}.hlist li:​last-child:​after{content:​none}.hlist .hlist{display:​inline;​padding-left:​1em}img{max-width:​100%}td{padding:​0 .5em}.admonition{padding:​.1em .5em;​margin-bottom:​1em}.admonition-title{font-weight:​bold}.admonition.note,​.admonition.info,​.admonition.important{background:#​aef}.admonition.todo,​.admonition.versionadded,​.admonition.tip,​.admonition.hint{background:#​dfd}.admonition.warning,​.admonition.versionchanged,​.admonition.deprecated{background:#​fd4}.admonition.error,​.admonition.danger,​.admonition.caution{background:​lightpink}</​style>​ 
 +<style media="​screen and (min-width: 700px)">​@media screen and (min-width:​700px){#​sidebar{width:​35%;​position:​sticky;​top:​0}#​content{width:​70%;​max-width:​100ch;​padding:​3em 4em;​border-left:​1px solid #ddd}pre code{font-size:​1em}.item .name{font-size:​1em}main{display:​flex;​flex-direction:​row-reverse;​justify-content:​flex-end}.toc ul ul,#index ul{padding-left:​1.5em}.toc > ul > li{margin-top:​.5em}}</​style>​ 
 +<style media="​print">​@media print{#​sidebar h1{page-break-before:​always}.source{display:​none}}@media print{*{background:​transparent !important;​color:#​000 !important;​box-shadow:​none !important;​text-shadow:​none !important}a[href]:​after{content:"​ (" attr(href) ")";​font-size:​90%}a[href][title]:​after{content:​none}abbr[title]:​after{content:"​ (" attr(title) "​)"​}.ir a:​after,​a[href^="​javascript:"​]:​after,​a[href^="#"​]:​after{content:""​}pre,​blockquote{border:​1px solid #​999;​page-break-inside:​avoid}thead{display:​table-header-group}tr,​img{page-break-inside:​avoid}img{max-width:​100!important}@page{margin:​0.5cm}p,​h2,​h3{orphans:​3;​widows:​3}h1,​h2,​h3,​h4,​h5,​h6{page-break-after:​avoid}}</style> 
 +<script defer src="​https:​//​cdnjs.cloudflare.com/​ajax/​libs/​highlight.js/​10.1.1/​highlight.min.js"​ integrity="​sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8="​ crossorigin></script
 +<script>window.addEventListener('​DOMContentLoaded',​ () => hljs.initHighlighting())</script> 
 +</head> 
 +<body> 
 +<main> 
 +<article id="​content">​ 
 +<​header>​ 
 +<h1 class="​title">​Module <​code>​vizypowerboard</code></​h1>​ 
 +</​header>​ 
 +<section id="​section-intro">​ 
 +<p>​This ​module is used with the Vizy Power Board, which is a printed circuit 
 +board that plugs into the Raspberry Pi I/O connector and is included 
 +with the Vizy camera. 
 +More information 
 +about Vizy can be found <a href="​https://​vizycamcam.com"​>here</​a>​.</p> 
 +<details class="​source">​ 
 +<​summary>​ 
 +<​span>​Expand source code</span
 +</​summary>​ 
 +<​pre><​code class="​python">#​ Copyright (C) 2021 Charmed Labs LLC 
 +
 +# begin license header 
 +
 +# This file is part of Vizy Software.  
 +
 +# This source code is provided under the terms of the 
 +# GNU General Public License v2 (http://​www.gnu.org/​licenses/​gpl-2.0.html). 
 +# Those wishing to use this source code, software and/or 
 +# technologies under different licensing terms should contact us at 
 +# [email protected].  
 +
 +# end license header 
 +&#​34;&#​34;&#​34;​ 
 +This module is used with the Vizy Power Board, which is a printed circuit  
 +board that plugs into the Raspberry Pi I/O connector and is included 
 +with the Vizy camera. ​ More information  
 +about Vizy can be found [here](https://​vizycamcam.com). 
 +&#​34;&#​34;&#​34; ​    
 +import smbus 
 +import RPi.GPIO as GPIO 
 +import time 
 +import datetime 
 +import os
  
-<​HTML><​p></​HTML>''​%%bcd2decimal(bcd)%%''​ :<​HTML></​p></​HTML>​ +FILE_VERSION = 1 
-<​HTML><​p></​HTML>''​%%decimal2bcd(dec)%%''​ :<​HTML></​p></​HTML>​ +COMPAT_HW_VERSION ​[3, 0]
-<​HTML><​p></​HTML>''​%%int8(i)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%uint16(i)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%uint8(i)%%''​ :<​HTML></​p></​HTML>​ +
-==== Methods ====+
  
-<​HTML><​p></​HTML>''​%%button(self)%%''​ :<​HTML></​p></​HTML>​ +ERROR_GENERIC ​255 
-<​HTML><​p></​HTML>''​%%button_click(self)%%''​ :<​HTML></​p></​HTML>​ +ERROR_BUSY ​254 
-<​HTML><​p></​HTML>''​%%button_power_off(self)%%''​ :<​HTML></​p></​HTML>​ +ERROR_READ_ONLY ​253 
-<​HTML><​p></​HTML>''​%%buzzer(self,​ freq, on=250, off=250, count=1, shift=0)%%''​ :<​HTML></​p></​HTML>​ +ERROR_NOT_SUPPORTED ​252
-<​HTML><​p></​HTML>''​%%dip_switches(self,​ val=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%fan(self,​ speed=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%fw_version(self)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%hw_version(self)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%io_bits(self,​ bits=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%io_set_mode(self,​ bit, mode=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%ir_filter(self,​ state=None, duration=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%led(self,​ r=0, g=0, b=0, flashes=0, repeat=False,​ atten=255, on=100, off=100, pause=200)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%led_background(self,​ r, g, b)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%led_unicorn(self,​ speed=10)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%measure(self,​ channel)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​dl></​HTML>​ +
-<​HTML><​dt></​HTML>''​%%power_off(self,​ t=5000)%%''<​HTML></​dt></​HTML>​ +
-<​HTML><​dd></​HTML>​Initiate powering off of device. Setting //t// (milliseconds) will delay that amount before switching off to give the system time to shut down resources.<​HTML></​dd></​HTML><​HTML></​dl></​HTML>​+
  
-<​HTML><​p></​HTML>''​%%power_on_date(self)%%''​ :<​HTML></​p></​HTML>​ +EXEC_OFFSET = 0xc0 
-<​HTML><​p></​HTML>''​%%power_on_seconds(self,​ seconds=None)%%''​ :<​HTML></​p></​HTML>​ +EXEC_RW ​0x80 
-<​HTML><​p></​HTML>''​%%resource_url(self)%%''​ :<​HTML></​p></​HTML>​ +EXEC_WRITE = 0x80 
-<​HTML><​p></​HTML>''​%%rtc(self)%%''​ :<​HTML></​p></​HTML>​ +EXEC_NVCONFIG = 1 
-<​HTML><​p></​HTML>''​%%rtc_adjust(self,​ val=None)%%''​ :<​HTML></​p></​HTML>​ +EXEC_AD ​2 
-<​HTML><​p></​HTML>''​%%rtc_set_system_datetime(self)%%''​ :<​HTML></​p></​HTML>​ +EXEC_DA ​3 
-<​HTML><​p></​HTML>''​%%status(self)%%''​ :<​HTML></​p></​HTML>​ +EXEC_RTC ​16 
-<​HTML><​p></​HTML>''​%%uuid(self)%%''​ :<​HTML></​p></​HTML>​ +EXEC_RTC_CALIBRATE = 17
-<​HTML><​p></​HTML>''​%%vcc12(self,​ state=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%vcc5(self,​ state=None)%%''​ :<​HTML></​p></​HTML>​ +
-<​HTML><​p></​HTML>''​%%wait_until_not_busy(self)%%''​ :<​HTML></​p></​HTML><​HTML></​dd></​HTML><​HTML></​dl></​HTML>​+
  
 +IO_MODE_INPUT = 0
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.io_set_mode()`.&#​34;&#​34;&#​34;​
 +IO_MODE_OUTPUT = 0x80 
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.io_set_mode()`.&#​34;&#​34;&#​34;​
 +IO_MODE_HIGH_CURRENT = IO_MODE_OUTPUT | 0x40
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.io_set_mode()`.&#​34;&#​34;&#​34;​
 +
 +DIPSWITCH_EXT_BUTTON = 0x01
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +DIPSWITCH_MUTE_BUZZER = 0x02
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +DIPSWITCH_NO_BG_LED = 0x04
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +DIPSWITCH_POWER_DEFAULT_OFF = 0x00
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +DIPSWITCH_POWER_DEFAULT_ON = 0x40
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +DIPSWITCH_POWER_SWITCH = 0x80
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +DIPSWITCH_POWER_PLUG = 0xc0
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.dip_switches()`.&#​34;&#​34;&#​34;​
 +
 +POWER_ON_SOURCE_ALARM = 0x01
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.power_on_source()`.&#​34;&#​34;&#​34;​
 +POWER_ON_SOURCE_POWER_BUTTON = 0x02
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.power_on_source()`.&#​34;&#​34;&#​34;​
 +POWER_ON_SOURCE_12V = 0x03
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.power_on_source()`.&#​34;&#​34;&#​34;​
 +POWER_ON_SOURCE_5V = 0x04
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.power_on_source()`.&#​34;&#​34;&#​34;​
 +
 +CHANNEL_VIN = 4
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.measure()`.&#​34;&#​34;&#​34;​
 +CHANNEL_5V = 5
 +&#​34;&#​34;&#​34;​Used with `VizyPowerBoard.measure()`.&#​34;&#​34;&#​34;​
 +
 +class VizyPowerBoard:​
 +    &#​34;&#​34;&#​34;​
 +    This class may be
 +    instantiated by more than one process. ​ The vizy-powerd service
 +    instantiates this class and uses it to monitor and control things such 
 +    as power-off requests, CPU temperature,​ fan speed, etc.  ​
 +    User programs can also instantiate this class and
 +    use its methods simultaneously.
 +    &#​34;&#​34;&#​34; ​   ​
 +    def __init__(self,​ addr=0x14, bus=1):
 +        &#​34;&#​34;&#​34;​
 +        Args:
 +          addr (integer, optional, default=0x14):​ I2C address of the board
 +          bus (integer, optional, default=1): the I2C bus number ​
 +
 +        &#​34;&#​34;&#​34;​
 +        self.bus = smbus.SMBus(bus)
 +        self.addr = addr
 +        hwv = self.hw_version()
 +        if hwv!=COMPAT_HW_VERSION:​
 +            raise RuntimeError(&#​34;​The hardware version of your Vizy Power Board (&#34; + str(hwv[0])+&#​39;​.&#​39;​+str(hwv[1]) + &#34;) is incompatible with this software file (&#34; + str(COMPAT_HW_VERSION[0])+&#​39;​.&#​39;​+str(COMPAT_HW_VERSION[1]) + &#​34;​).&#​34;​)
 +
 +    @staticmethod
 +    def _bcd2decimal(bcd):​
 +        tens = (bcd&​amp;​0xf0)&​gt;&​gt;​4
 +        ones = bcd&​amp;​0x0f
 +        return tens*10 + ones
 +
 +    @staticmethod
 +    def _decimal2bcd(dec):​
 +        tens = int(dec/10)
 +        ones = dec%10
 +        return (tens&​lt;&​lt;​4) | ones
 +
 +    @staticmethod
 +    def _u_int8(i):
 +        i = round(i)
 +        if i&​gt;​0xff:​
 +            return 0xff
 +        if i&lt;0:
 +            return 0
 +        return i
 +
 +    @staticmethod
 +    def _int8(i):
 +        i = round(i)
 +        if i&​gt;​0x7f:​
 +            return 0x7f
 +        if i&​lt;​-0x80:​
 +            return 0x80
 +        if i&lt;0:
 +            return 0x100+i
 +        return i
 +
 +    @staticmethod
 +    def _uint16(i):
 +        i = round(i)
 +        if i&​gt;​0xffff:​
 +            return 0xffff
 +        if i&lt;0:
 +            return 0
 +        return i
 +
 +    def _status(self):​
 +        return self.bus.read_i2c_block_data(self.addr,​ 0, 1)[0]
 +
 +    def _wait_until_not_busy(self):​
 +        while self._status()==ERROR_BUSY:​
 +            time.sleep(0.001)
 +
 +    def hw_version(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the major and minor versions of the PCB as a 2-item list.
 +        &#​34;&#​34;&#​34; ​
 +        return self.bus.read_i2c_block_data(self.addr,​ 1, 2)
 +
 +    def fw_version(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the major, minor and build versions of the firmware as 
 +        a 3-item list.
 +        &#​34;&#​34;&#​34; ​
 +        return self.bus.read_i2c_block_data(self.addr,​ 3, 3)
 +
 +    def resource_url(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the url of a JSON file that contains information about 
 +        resources, such as the location of the latest version of this code, 
 +        latest firmware, etc.
 +        &#​34;&#​34;&#​34;​
 +        chars = self.bus.read_i2c_block_data(self.addr,​ 6, 32)
 +        s = &#​39;&#​39;​
 +        for c in chars:
 +            if c==0: # read up to the null character
 +                break
 +            s += chr(c)
 +        return s
 +
 +    def uuid(self):
 +        &#​34;&#​34;&#​34;​
 +        Returns a 16-byte unique ID that can be used an a unique
 +        ID for your Vizy camera. ​ This unique ID is stored on the Vizy Power
 +        Board and remains constant regardless of firmware upgrades, etc.
 +        &#​34;&#​34;&#​34;​
 +        return self.bus.read_i2c_block_data(self.addr,​ 22, 16)
 +
 +    def power_off_requested(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns `True` if Vizy&#​39;​s button is held down for more than 5 seconds
 +        indicating that the user wishes to initiate safe shutdown and power
 +        off.  Returns `False` otherwise.
 +
 +        This is used by the vizy-powerd service. ​
 +        &#​34;&#​34;&#​34;​
 +        button = self.bus.read_i2c_block_data(self.addr,​ 38, 1)
 +        if button[0]==0x0f:​
 +            return True
 +        else:
 +            return False
 +
 +    def power_off(self,​ t=5000):
 +        &#​34;&#​34;&#​34;​
 +        Powers Vizy off. The `t` argument specifies how long 
 +        to wait before turning off (specified in milliseconds). ​ The 
 +        vizy-powerd service calls this upon shutdown.
 +        &#​34;&#​34;&#​34;​
 +        self.bus.write_i2c_block_data(self.addr,​ 38, [0x1f, int(t/​100)])
 +
 +    def power_on_alarm_date(self,​ datetime_=None):​
 +        &#​34;&#​34;&#​34;​
 +        If you wish to power off your Vizy and have it &#​34;​wake up&#34; at a
 +        specified time and date, call this method with the desired
 +        datetime object and initiate a shutdown. (e.g. `sudo shutdown now`).
 +
 +
 +        The code below tells Vizy to turn on on December 2, 2022, 11:18am.
 +
 +            import vizypowerboard as vpb
 +            from datetime import datetime
 +            v = vpb.VizyPowerBoard()
 +            d=datetime(year=2022,​ month=12, day=2, hour=11, minute=18, second=0)
 +            v.power_on_alarm_date(d)
 +
 +        Args:
 +          datetime_ (datetime, optional, default=None):​ `datetime` object that
 +            specifies the date/time to &#​34;​wake up&#34; (turn on).
 +
 +        Returns:
 +          Calling without a datetime object returns a `datetime` object ​
 +          reflecting the active alarm time.  If there is no active alarm,
 +          `None` is returned.
 +           
 +        Notes:
 +          * Once setting the alarm date, Vizy will retain it even if Vizy loses
 +          power for extended periods of time.
 +          * If the alarm expires while Vizy is on, Vizy will emit a buzzer tone
 +          and remain on.
 +          * If the alarm expires while Vizy is off (but plugged into and
 +          receiving power), Vizy will turn on.
 +          * If the alarm expires while Vizy is unplugged from (or not receiving)
 +          power, Vizy will turn on as soon as it receives power.  ​
 +        &#​34;&#​34;&#​34;​
 +        if datetime_ is None:
 +            t = self.bus.read_i2c_block_data(self.addr,​ 41, 6)
 +            if t[5]==0:
 +                return None
 +            return datetime.datetime(year=self._bcd2decimal(t[5])+2016,​ month=self._bcd2decimal(t[4]),​ day=self._bcd2decimal(t[3]),​ hour=self._bcd2decimal(t[2]),​ minute=self._bcd2decimal(t[1]),​ second=self._bcd2decimal(t[0]))
 +        t = [self._decimal2bcd(datetime_.second),​ self._decimal2bcd(datetime_.minute),​ self._decimal2bcd(datetime_.hour),​ self._decimal2bcd(datetime_.day),​ self._decimal2bcd(datetime_.month),​ self._decimal2bcd(datetime_.year-2016)]
 +        self.bus.write_i2c_block_data(self.addr,​ 41, t)
 +    ​
 +
 +    def power_on_alarm_seconds(self,​ seconds=None):​
 +        &#​34;&#​34;&#​34;​
 +        Allows you to specify a power on alarm in seconds in the future. ​
 +        For example, if you wish for Vizy to turn back on in 5 minutes, you
 +        would call `power_on_alarm_seconds(300)` and then initiate a shutdown.
 +        See `VizyPowerBoard.power_on_alarm_date()` for more information about the power on 
 +        alarm.
 +
 +        Args:
 +          seconds (integer, optional, default=None):​ Number of seconds in the
 +            future you wish Vizy to turn on in.
 +
 +        Returns:
 +          Calling this method without arguments returns the number of seconds
 +          until the alarm expires. ​ If no alarm is pending, `None` is returned.
 +        &#​34;&#​34;&#​34;​
 +        if seconds is None:
 +            pod = self.power_on_alarm_date()
 +            if pod is None:
 +                return None
 +            diff = pod - self.rtc()
 +            return diff.days*86400+diff.seconds ​
 +        # Add seconds to current time and set power on alarm    ​
 +        self.power_on_alarm_date(self.rtc()+datetime.timedelta(seconds=seconds))
 +
 +    def power_on_source(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the source of what turned on Vizy for the current power cycle.
 +        It is one of either:
 +
 +        * POWER_ON_SOURCE_ALARM,​ indicates that Vizy was powered on
 +        by the power on alarm expiring. ​ See power_on_alarm_date() and
 +        power_on_alarm_seconds().
 +        * POWER_ON_SOURCE_POWER_BUTTON,​ indicates that Vizy was powered ​
 +        on by someone pressing the button.
 +        * POWER_ON_SOURCE_12V = indicates that Vizy was powered on
 +        by power being applied to 12V power input. ​ This only applies if the
 +        dip switch power mode allows powering on by plugging in power via the
 +        12V power input.
 +        * POWER_ON_SOURCE_5V,​ indicates that Vizy was powered on by applying
 +        power to the Raspberry Pi&#​39;​s USB-C power input.
 +        &#​34;&#​34;&#​34;​
 +        source = self.bus.read_i2c_block_data(self.addr,​ 40, 1)[0]
 +        return source
 +
 +    def button(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns `True` if the button is being pressed currently, `False` otherwise.
 +        &#​34;&#​34;&#​34;​
 +        button = self.bus.read_i2c_block_data(self.addr,​ 47, 1)
 +        if button[0]&​amp;​0x02:​
 +            return True
 +        else:
 +            return False
 +
 +    def button_pressed(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns `True` if the button was pressed within the last 5 seconds,
 +        `False` otherwise. ​ This is useful if the polling is intermittant or
 +        slow, as button presses are not missed (as long as you check at least
 +        every 5 seconds!)
 +        &#​34;&#​34;&#​34;​
 +        button = self.bus.read_i2c_block_data(self.addr,​ 47, 1)
 +        if button[0]&​amp;​0x01:​
 +            # Reset bit
 +            self.bus.write_i2c_block_data(self.addr,​ 47, [0])
 +            return True
 +        else:
 +            return False
 +
 +    def vcc12(self, state=None):​
 +        &#​34;&#​34;&#​34;​
 +        If `state` is `True`, the 12V output on Vizy&#​39;​s I/O connector (pin 2) will be enabled and output 12V.  If `state` is `False`, the 12V output
 +        will be disabled. ​ Calling without arguments returns its current state.
 +        &#​34;&#​34;&#​34; ​
 +        config = self.bus.read_i2c_block_data(self.addr,​ 48, 1)
 +        if state is None:
 +            return True if config&​amp;​0x01 else False
 +        if state:
 +            config[0] |= 0x01
 +        else:
 +            config[0] &amp;= ~0x01
 +
 +        self.bus.write_i2c_block_data(self.addr,​ 48, config)
 +
 +    def vcc5(self, state=None):​
 +        &#​34;&#​34;&#​34;​
 +        If `state` is `True`, the 5V output on Vizy&#​39;​s I/O connector (pin 3) will
 +        be enabled and output 5V.  If `state` is `False`, the 5V output will be
 +        disabled. ​ Calling without arguments returns its current state.
 +        &#​34;&#​34;&#​34; ​
 +        if state is None:
 +            return True if config&​amp;​0x02 else False
 +        if state:
 +            config[0] |= 0x02
 +        else:
 +            config[0] &amp;= ~0x02
 +
 +        self.bus.write_i2c_block_data(self.addr,​ 48, config)
 +
 +    def led(self, r=0, g=0, b=0, flashes=0, repeat=False,​ atten=255, on=100, off=100, pause=200):
 +        &#​34;&#​34;&#​34;​
 +        Controls the RGB LED in one of several modes:
 +
 +        * **Continuous**:​ just setting `r`, `g`, and `b` will set set the LED color
 +        and turn it on continuously. ​ `r`, `g`, and `b` values range between 0 
 +        and  255.
 +
 +                led(255, 0, 0)   # turn on LED, red color
 +                led(255, 255, 0) # turn on LED, yellow color
 +                led(0, 0, 255)   # turn on LED, blue color
 +                led(0, 0, 0)     # turn off LED
 +
 +        * **Flashes**:​ setting the `flashes` value to a non-zero value will
 +        cause the LED to flash the indicated number of times. ​ You can also
 +        specify the `on` and `off` arguments to indicate the amount of time 
 +        the LED is on and off for each flash (specified in milliseconds).
 +
 +                led(0, 0, 255, 3)  # flash blue 3 times (then stop)
 +                led(0, 0, 255, 3, on=500, off=500) ​ # flash blue 3 times, much more slowly
 +
 +        * **Repeated flashes**: setting the `repeat` argument to `True` ​
 +        will cause the indicated flash pattern to repeat forever. ​ You can
 +        modify the pause time between flash sequences by setting `pause`
 +        (milliseconds).
 +
 +                led(0, 0, 255, 3, True, pause=500) ​ # flash blue 3 times, pause, then repeat
 +                led(0, 0, 255, repeat=True,​ on=500, off=500)` ​ # flash blue forever
 +
 +        * **Flashing with attenuation**:​ you can also set the `atten` ​
 +        argument to make the LED to turn on and off slowly, like an
 +        incandescent light. ​ The value is the rate of change, so lower values
 +        cause the LED color to change more slowly. ​
 +
 +                led(0, 0, 255, repeat=True,​ atten=10, on=500, off=500) # flash blue forever, but turn on and turn off very slowly
 +        &#​34;&#​34;&#​34;​
 +        on = self._u_int8(on/​10)
 +        off = self._u_int8(off/​10)
 +        pause = self._u_int8(pause/​10)
 +        if flashes==0:
 +            mode = 0
 +        if repeat:
 +            mode = 0x02
 +        else:
 +            mode = 0x01
 +        self.bus.write_i2c_block_data(self.addr,​ 49, [mode, self._u_int8(r),​ self._u_int8(g),​ self._u_int8(b),​
 +            on, off, self._u_int8(flashes),​ pause, self._u_int8(atten)])
 +
 +    def led_unicorn(self,​ speed=10):
 +        &#​34;&#​34;&#​34;​
 +        This causes the LED to change color in succession: red, orange, yellow, ​
 +        green, cyan, blue, violet and then repeat again. ​ The `speed` argument ​
 +        ranges between 0 and 10.  For example, a `speed` of 0 causes the color 
 +        to change once every couple of seconds. ​ A `speed` of 10 causes the color to change about 6 times per second.
 +        &#​34;&#​34;&#​34;​
 +        if speed&​gt;​10:​
 +            speed = 10
 +        elif speed&​lt;​0:​
 +            speed = 0
 +
 +        on = self._u_int8(10 + (10-speed)*140/​10)
 +        atten = self._u_int8(3 + speed*47/​10) ​   ​
 +        self.bus.write_i2c_block_data(self.addr,​ 49, [0x08, 0, 0, 0, on, 0, 0, 0, atten])
 +
 +    def led_background(self,​ r=-1, g=-1, b=-1):
 +        &#​34;&#​34;&#​34;​
 +        The &#​34;​background&#​34;​ LED color is the color of the LED when the LED is 
 +        turned &#​34;​off&#​34;​. ​ It is used by system programs such as vizy-powerd to
 +        indicate Vizy&#​39;​s system state such as, booting (yellow), finished
 +        booting (green), running server (blue), etc.  Note, the background
 +        color does not influence the LED colors set by calls to led().
 +
 +        Calling led_background() without arguments returns the current ​
 +        background color r, g, and b values in a list. 
 +
 +            led_background(48,​ 48, 0)  # set background color to yellow
 +            led(0, 255, 0)  # turn on LED, green (as expected)
 +            led(0, 0, 0)  # turn LED off, and restore background color (yellow as set previously) ​   ​
 +        &#​34;&#​34;&#​34;​
 +        if r==-1:
 +            return self.bus.read_i2c_block_data(self.addr,​ 58, 3)
 +        self.bus.write_i2c_block_data(self.addr,​ 58, [self._u_int8(r),​ self._u_int8(g),​ self._u_int8(b)])
 + 
 +
 +    def buzzer(self,​ freq, on=250, off=250, count=1, shift=0):
 +        &#​34;&#​34;&#​34;​
 +        Emit tones through the buzzer. ​ The `freq` argument sets the frequency ​
 +        of the tone in Hz and the `on` argument sets the length of the tone in
 +        milliseconds.  ​
 +
 +        If you wish to emit more than 1 tone, you can set the
 +        `count` argument to the desired number.  ​
 +
 +        The `off` argument sets 
 +        the pause between tones in milliseconds. ​ The `shift` argument is a
 +        value ranging between -128 and 127 that causes the tone&#​39;​s frequency to
 +        raise if `shift` is greater than 0, or descend if `shift` is less 
 +        than 0.
 +
 +            buzzer(2000,​ 500) # emit a 2000Hz tone for 500ms
 +            buzzer(1000,​ count=3) # emit a 1000Hz tone 3 times   
 +            buzzer(1000,​ 500, 100, 3) # emit a longer 1000Hz tone 3 times
 +            buzzer(500, 250, 0, 10, 50) # emit 10 warbling tones like a siren
 +        &#​34;&#​34;&#​34;​
 +        freq = self._uint16(freq)
 +        f0 = freq&​amp;​0xff
 +        f1 = (freq&​gt;&​gt;​8)&​amp;​0xff
 +        self.bus.write_i2c_block_data(self.addr,​ 61, [0, f0, f1, self._u_int8(on/​10),​ self._u_int8(off/​10), ​
 +            self._u_int8(count),​ self._int8(shift)])
 +
 +
 +    def io_set_mode(self,​ bit, mode=None):
 +        &#​34;&#​34;&#​34;​
 +        Sets or gets the io mode of the given bit.  The `bit` argument ranges
 +        between ​ 0 and 3 and corresponds to pins 4 through 7 on Vizy&#​39;​s IO
 +        connector. Calling this method with no mode argument returns the mode
 +        of the given bit, otherwise, the `mode` argument can be one of 
 +        the following:
 +
 +        * IO_MODE_INPUT,​ sets the bit to high impedance input mode with 
 +        a weak pull-up resistor to 3.3V.  The input voltage can range between
 +        0 and Vin where Vin is the supply voltage. ​ Voltages lower than 1V 
 +        read as logic 0 via `VizyPowerBoard.io_bits()`. ​ Voltages higher ​
 +        than 1V are read as logic 1.
 +        * IO_MODE_OUTPUT,​ sets the bit to output mode.  If the bit is set to
 +        logic 0 via `VizyPowerBoard.io_bits()`,​ the output voltage is 0V.  ​
 +        If the bit is set to logic 1, the output voltage is 3.3V.  In this mode, each bit can source and sink 5mA.  ​
 +        * IO_MODE_HIGH_CURRENT,​ sets the bit to a special high current mode
 +        that allows the bit to sink as much as 1000mA, when the bit 
 +        set to logic 0 via `VizyPowerBoard.io_bits()`. ​ Otherwise, this mode
 +        behaves exactly as IO_MODE_OUTPUT. ​   ​
 +        &#​34;&#​34;&#​34;​
 +        if mode is None:
 +            return self.bus.read_i2c_block_data(self.addr,​ 68+bit, 1)[0]
 +        if bit==2:
 +            # set GPIO14 (UART TXD) as input so it doesn&#​39;​t conflict
 +            GPIO.setwarnings(False)
 +            GPIO.setmode(GPIO.BOARD)
 +            GPIO.setup(8,​ GPIO.IN)
 +        self.bus.write_i2c_block_data(self.addr,​ 68+bit, [self._u_int8(mode)])
 +
 +    def io_bits(self,​ bits=None):
 +        &#​34;&#​34;&#​34;​
 +        Sets or gets the logic state of the IO bits 0 through 3, corresponding
 +        to pins 4 through 7 on Vizy&#​39;​s IO connector. ​ The `bits` argument ranges
 +        between 0 and 15 as it is a binary representation of the logic state
 +        of the 4 IO bits.  If the `bits` argument isn&#​39;​t specified, the logic
 +        state of the 4 bits are returned.
 +
 +            io_bits(1) ​  # set IO bit 0 to logic 1 and bits 1, 2, 3 to logic 0
 +            io_bits(10) ​ # set IO bits 1 and 3 to logic 1 and bits 0 and 2 to logic 0
 +            bits = io_bits() ​ # get logic state of IO bits
 +            io_bits(io_bits()|1) ​ # set IO bit 0 to logic 1, leave bits 1, 2, 3 unchanged. ​
 +        &#​34;&#​34;&#​34;​
 +        if bits is None:
 +            return self.bus.read_i2c_block_data(self.addr,​ 72, 1)
 +        self.bus.write_i2c_block_data(self.addr,​ 72, [bits])
 +
 +
 +    def ir_filter(self,​ state=None, duration=None):​
 +        &#​34;&#​34;&#​34;​
 +        Actuates the electro-mechanical IR-cut filter on Vizy&#​39;​s camera. ​ Vizy
 +        uses a CMOS sensor which is very sensitive to IR light. ​ IR light can 
 +        adversely affect color fidelity during the daytime so an IR-cut filter
 +        is used to block the IR light (`state`=True). ​ During nighttime IR light
 +        is typically used as a discreet method of illumination and the IR-cut
 +        filter is removed (`state`=False). ​ If the `state` argument is `True`,
 +        the filter is actuated in place (and will stay there) until another
 +        call is made with the state argument set to `False` (in which case the
 +        IR-cut filter will be removed).  ​
 +
 +        The `duration` argument is optional and
 +        controls how long (in milliseconds) the actuation coil receives power.
 +
 +        Calling this method without arguments returns the state of IR-cut
 +        filter.
 +        &#​34;&#​34;&#​34;​
 +        if state is None:
 +            return True if self.bus.read_i2c_block_data(self.addr,​ 73, 1)[0] else False
 +        data = [1] if state else [0]
 +        if duration is not None:
 +            data.append(int(duration/​10))
 +        self.bus.write_i2c_block_data(self.addr,​ 73, data)
 +
 +    def fan(self, speed=None):​
 +        &#​34;&#​34;&#​34;​
 +        Set or get the fan speed. ​ The `speed` argument can range between 0 
 +        and 12 where 0 is off and 12 is maximum speed. ​ The fan speed 
 +        is typically regulated automatically by vizy-powerd.
 +
 +        Calling this method without arguments returns the current fan speed.  ​
 +        &#​34;&#​34;&#​34;​
 +        if speed is None:
 +            return self.bus.read_i2c_block_data(self.addr,​ 75, 1)[0]
 +        self.bus.write_i2c_block_data(self.addr,​ 75, [self._u_int8(speed)])
 +       
 +
 +    def rtc(self, datetime_=None):​
 +        &#​34;&#​34;&#​34;​
 +        Set or get the real-time clock time/​date. ​ The Vizy power board has a 
 +        battery-backed real-time clock that keeps track of time/date, power 
 +        alarms, etc. even while Vizy is receiving no power. ​ Passing in a
 +        datetime object sets the time/​date.  ​
 +
 +        Calling this method with no
 +        arguments returns a datetime object representing the current ​
 +        date/​time. ​
 +
 +        For example, the code below sets the date to December 2, 2020, 11:18am:
 +
 +            from datetime import datetime
 +            import vizypowerboard as vpb
 +            v = vpb.VizyPowerBoard()
 +            t = datetime(year=2020,​ month=12, day=2, hour=11, minute=18, second=0)
 +            v.rtc(t)
 +        &#​34;&#​34;&#​34;​
 +        if datetime_ is None:
 +            # Initiate RTC retrieval.
 +            self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC])
 +            # Wait until it&#​39;​s ready.
 +            self._wait_until_not_busy()
 +
 +            t = self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 8)
 +            return datetime.datetime(year=self._bcd2decimal(t[7])+2016,​ month=self._bcd2decimal(t[6]),​ day=self._bcd2decimal(t[4]),​ hour=self._bcd2decimal(t[3]),​ minute=self._bcd2decimal(t[2]),​ second=self._bcd2decimal(t[1]))
 + 
 +        t = [EXEC_RTC|EXEC_WRITE,​ 0, self._decimal2bcd(datetime_.second),​ self._decimal2bcd(datetime_.minute),​ self._decimal2bcd(datetime_.hour),​ self._decimal2bcd(datetime_.day),​ 0, self._decimal2bcd(datetime_.month),​ self._decimal2bcd(datetime_.year-2016)]
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ t)
 +        self._wait_until_not_busy()
 +
 +    def dip_switches(self,​ val=None):
 +        &#​34;&#​34;&#​34;​
 +        Set or get the (virtual) DIP switch state. ​ The DIP switches are a set 
 +        of &#​34;​switches&#​34;​ that allow you to control Vizy&#​39;​s power-on or power-off
 +        behavior. ​ Once they are set, they will retain the setting even if 
 +        power is removed. ​
 +        The switches are a set of values that can be ORed together:
 +
 +        * DIPSWITCH_EXT_BUTTON,​ used to set external/​remote power button, ​
 +        e.g. with outdoor enclosure. ​ Default disabled.
 +        * DIPSWITCH_MUTE_BUZZER,​ used to mute the buzzer. ​ Default disabled.
 +        * DIPSWITCH_NO_BG_LED,​ used to disable the background LED, which is 
 +        normally set to yellow upon power up.  Default disabled.
 +        * DIPSWITCH_POWER_DEFAULT_OFF,​ if this power mode is set and you 
 +        plug in power via the 12V power input, Vizy will remain off by default
 +        until you press the button to power Vizy on.  And if power is
 +        interrupted while Vizy is on, *Vizy will turn off*.  If power is
 +        interrupted while Vizy is off, Vizy will remain off.  This is the
 +        default power mode.
 +        * DIPSWITCH_POWER_DEFAULT_ON,​ if this power mode is set and you 
 +        plug in power via the 12V power input, Vizy will turn on by default
 +        without pressing the button. ​ And if power is interrupted while Vizy
 +        is on, Vizy will reset, but remain on.  If power is interrupted while
 +        Vizy is off, *Vizy will turn on*.  Default disabled.
 +        * DIPSWITCH_POWER_SWITCH,​ if this power mode is set and you plug in
 +        power via the 12V power input, Vizy will remain off (as in
 +        DIPSWITCH_POWER_DEFAULT_OFF mode), unless power was 
 +        removed while Vizy was on.  In this case Vizy will turn on when you
 +        (re)apply power. ​ If power is interrupted while Vizy is off, Vizy 
 +        will remain off.  This behavior is similar to the behavior of a real
 +        power switch in that it retains the power &#​34;​state&#​34;​ (on or off) and acts
 +        accordingly. ​ Default disabled.
 +        * DIPSWITCH_POWER_PLUG,​ if this power mode is set Vizy will remain
 +        powered on as long as it receives power through the 12V power plug, 
 +        and you will not be able to turn off Vizy via button or software as
 +        long as it&#​39;​s plugged in and receiving power. ​ Default disabled.
 +        ​
 +        For example: ​
 +
 +            dip_switches(DIPSWITCH_EXT_BUTTON | DIPSWITCH_POWER_SWITCH) # set external power button and power switch mode
 +        &#​34;&#​34;&#​34;​
 +        if val is None:
 +            self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_NVCONFIG])
 +            # Wait until it&#​39;​s ready.
 +            self._wait_until_not_busy()
 +            return self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 1)[0]
 +
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_NVCONFIG|EXEC_WRITE,​ self._u_int8(val)])
 +        self._wait_until_not_busy()
 +
 +    def rtc_adjust(self,​ val=None):
 +        &#​34;&#​34;&#​34;​
 +        Set or get the real-time clock adjustment. ​ Vizy&#​39;​s real-time clock 
 +        crystal has an accuracy of 20ppm, which means that it can lose or gain 
 +        up to 20 seconds for every 1 million elapsed seconds. ​ Normally, this
 +        isn&#​39;​t an issue, but if Vizy spends a lengthy period of time (months)
 +        without Internet access, it could lose or gain minutes, which 
 +        depending on the application could be significant. The adjustment
 +        value can offset this inaccuracy. ​ The `val` argument can range 
 +        between -128 and 127 and has a multiplier of 2.170 ppm.  ​
 +
 +        For example, ​
 +        if the RTC is gaining 10 seconds every 1 million seconds, you would 
 +        call `rtc_adjust(-5)`. ​ If the RTC is losing 10 seconds every million
 +        seconds you would call `rtc_adjust(5)`.
 +        ​
 +        The adjustment value is retained by the real-time clock even when 
 +        Vizy&#​39;​s power is removed. ​
 +        &#​34;&#​34;&#​34;​
 +        if val is None:
 +            self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC_CALIBRATE])
 +            # Wait until it&#​39;​s ready.
 +            self._wait_until_not_busy()
 +            return self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 1)[0]
 +
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC_CALIBRATE|EXEC_WRITE,​ self._int8(val)])
 +        self._wait_until_not_busy()
 +
 +
 +    def measure(self,​ channel):
 +        &#​34;&#​34;&#​34;​
 +        Get the voltage values of various channels. ​ The returned value is 
 +        the voltage measured (in Volts) of the given channel. ​ The `channel`
 +        argument can be one of the following:
 +
 +        * CHANNEL_VIN,​ this channel measures the voltage present at the 12V
 +        power input.
 +        * CHANNEL_5V, this channel measures the voltage present at the 5V
 +        voltage rail provided to the Raspberry Pi.
 +        &#​34;&#​34;&#​34;  ​
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_AD, self._u_int8(channel)])
 +        # Wait until it&#​39;​s ready.
 +        self._wait_until_not_busy()
 +        val = self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+2,​ 2)
 +        return (val[1]*0x100 + val[0])/​1000
 +
 +
 +    def rtc_set_system_datetime(self,​ datetime_=None):​
 +        &#​34;&#​34;&#​34;​
 +        A convenience method that sets the system time/date based on the 
 +        real-time time/​date. ​ This is called by vizy-powerd upon power-up. ​
 +        &#​34;&#​34;&#​34;​
 +        if os.geteuid()!=0:​
 +            raise PermissionError(&#​34;​You need root permission to set the time/​date.&#​34;​)
 +        if datetime_ is None:
 +            datetime_ = self.rtc()
 +        s = datetime_.isoformat()
 +        os.system(f&#​34;​sudo date -s {s}&#​34;​)</​code></​pre>​
 +</​details>​
 +</​section>​
 +<​section>​
 +</​section>​
 +<​section>​
 +<h2 class="​section-title"​ id="​header-variables">​Global variables</​h2>​
 +<dl>
 +<dt id="​vizypowerboard.CHANNEL_5V"><​code class="​name">​var <span class="​ident">​CHANNEL_5V</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.measure"​ href="#​vizypowerboard.VizyPowerBoard.measure">​VizyPowerBoard.measure()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.CHANNEL_VIN"><​code class="​name">​var <span class="​ident">​CHANNEL_VIN</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.measure"​ href="#​vizypowerboard.VizyPowerBoard.measure">​VizyPowerBoard.measure()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_EXT_BUTTON"><​code class="​name">​var <span class="​ident">​DIPSWITCH_EXT_BUTTON</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_MUTE_BUZZER"><​code class="​name">​var <span class="​ident">​DIPSWITCH_MUTE_BUZZER</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_NO_BG_LED"><​code class="​name">​var <span class="​ident">​DIPSWITCH_NO_BG_LED</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_POWER_DEFAULT_OFF"><​code class="​name">​var <span class="​ident">​DIPSWITCH_POWER_DEFAULT_OFF</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_POWER_DEFAULT_ON"><​code class="​name">​var <span class="​ident">​DIPSWITCH_POWER_DEFAULT_ON</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_POWER_PLUG"><​code class="​name">​var <span class="​ident">​DIPSWITCH_POWER_PLUG</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.DIPSWITCH_POWER_SWITCH"><​code class="​name">​var <span class="​ident">​DIPSWITCH_POWER_SWITCH</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​VizyPowerBoard.dip_switches()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.IO_MODE_HIGH_CURRENT"><​code class="​name">​var <span class="​ident">​IO_MODE_HIGH_CURRENT</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.io_set_mode"​ href="#​vizypowerboard.VizyPowerBoard.io_set_mode">​VizyPowerBoard.io_set_mode()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.IO_MODE_INPUT"><​code class="​name">​var <span class="​ident">​IO_MODE_INPUT</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.io_set_mode"​ href="#​vizypowerboard.VizyPowerBoard.io_set_mode">​VizyPowerBoard.io_set_mode()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.IO_MODE_OUTPUT"><​code class="​name">​var <span class="​ident">​IO_MODE_OUTPUT</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.io_set_mode"​ href="#​vizypowerboard.VizyPowerBoard.io_set_mode">​VizyPowerBoard.io_set_mode()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.POWER_ON_SOURCE_12V"><​code class="​name">​var <span class="​ident">​POWER_ON_SOURCE_12V</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_source"​ href="#​vizypowerboard.VizyPowerBoard.power_on_source">​VizyPowerBoard.power_on_source()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.POWER_ON_SOURCE_5V"><​code class="​name">​var <span class="​ident">​POWER_ON_SOURCE_5V</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_source"​ href="#​vizypowerboard.VizyPowerBoard.power_on_source">​VizyPowerBoard.power_on_source()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.POWER_ON_SOURCE_ALARM"><​code class="​name">​var <span class="​ident">​POWER_ON_SOURCE_ALARM</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_source"​ href="#​vizypowerboard.VizyPowerBoard.power_on_source">​VizyPowerBoard.power_on_source()</​a></​code>​.</​p></​div>​
 +</dd>
 +<dt id="​vizypowerboard.POWER_ON_SOURCE_POWER_BUTTON"><​code class="​name">​var <span class="​ident">​POWER_ON_SOURCE_POWER_BUTTON</​span></​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Used with <​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_source"​ href="#​vizypowerboard.VizyPowerBoard.power_on_source">​VizyPowerBoard.power_on_source()</​a></​code>​.</​p></​div>​
 +</dd>
 +</dl>
 +</​section>​
 +<​section>​
 +</​section>​
 +<​section>​
 +<h2 class="​section-title"​ id="​header-classes">​Classes</​h2>​
 +<dl>
 +<dt id="​vizypowerboard.VizyPowerBoard"><​code class="​flex name class">​
 +<​span>​class <span class="​ident">​VizyPowerBoard</​span></​span>​
 +<​span>​(</​span><​span>​addr=20,​ bus=1)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​This class may be
 +instantiated by more than one process.
 +The vizy-powerd service
 +instantiates this class and uses it to monitor and control things such
 +as power-off requests, CPU temperature,​ fan speed, etc.<​br>​
 +User programs can also instantiate this class and
 +use its methods simultaneously.</​p>​
 +<h2 id="​args">​Args</​h2>​
 +<dl>
 +<​dt><​strong><​code>​addr</​code></​strong>​ :&​ensp;<​code>​integer</​code>,​ optional, default=<​code>​0x14</​code></​dt>​
 +<​dd>​I2C address of the board</​dd>​
 +<​dt><​strong><​code>​bus</​code></​strong>​ :&​ensp;<​code>​integer</​code>,​ optional, default=<​code>​1</​code></​dt>​
 +<​dd>​the I2C bus number</​dd>​
 +</​dl></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​class VizyPowerBoard:​
 +    &#​34;&#​34;&#​34;​
 +    This class may be
 +    instantiated by more than one process. ​ The vizy-powerd service
 +    instantiates this class and uses it to monitor and control things such 
 +    as power-off requests, CPU temperature,​ fan speed, etc.  ​
 +    User programs can also instantiate this class and
 +    use its methods simultaneously.
 +    &#​34;&#​34;&#​34; ​   ​
 +    def __init__(self,​ addr=0x14, bus=1):
 +        &#​34;&#​34;&#​34;​
 +        Args:
 +          addr (integer, optional, default=0x14):​ I2C address of the board
 +          bus (integer, optional, default=1): the I2C bus number ​
 +
 +        &#​34;&#​34;&#​34;​
 +        self.bus = smbus.SMBus(bus)
 +        self.addr = addr
 +        hwv = self.hw_version()
 +        if hwv!=COMPAT_HW_VERSION:​
 +            raise RuntimeError(&#​34;​The hardware version of your Vizy Power Board (&#34; + str(hwv[0])+&#​39;​.&#​39;​+str(hwv[1]) + &#34;) is incompatible with this software file (&#34; + str(COMPAT_HW_VERSION[0])+&#​39;​.&#​39;​+str(COMPAT_HW_VERSION[1]) + &#​34;​).&#​34;​)
 +
 +    @staticmethod
 +    def _bcd2decimal(bcd):​
 +        tens = (bcd&​amp;​0xf0)&​gt;&​gt;​4
 +        ones = bcd&​amp;​0x0f
 +        return tens*10 + ones
 +
 +    @staticmethod
 +    def _decimal2bcd(dec):​
 +        tens = int(dec/10)
 +        ones = dec%10
 +        return (tens&​lt;&​lt;​4) | ones
 +
 +    @staticmethod
 +    def _u_int8(i):
 +        i = round(i)
 +        if i&​gt;​0xff:​
 +            return 0xff
 +        if i&lt;0:
 +            return 0
 +        return i
 +
 +    @staticmethod
 +    def _int8(i):
 +        i = round(i)
 +        if i&​gt;​0x7f:​
 +            return 0x7f
 +        if i&​lt;​-0x80:​
 +            return 0x80
 +        if i&lt;0:
 +            return 0x100+i
 +        return i
 +
 +    @staticmethod
 +    def _uint16(i):
 +        i = round(i)
 +        if i&​gt;​0xffff:​
 +            return 0xffff
 +        if i&lt;0:
 +            return 0
 +        return i
 +
 +    def _status(self):​
 +        return self.bus.read_i2c_block_data(self.addr,​ 0, 1)[0]
 +
 +    def _wait_until_not_busy(self):​
 +        while self._status()==ERROR_BUSY:​
 +            time.sleep(0.001)
 +
 +    def hw_version(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the major and minor versions of the PCB as a 2-item list.
 +        &#​34;&#​34;&#​34; ​
 +        return self.bus.read_i2c_block_data(self.addr,​ 1, 2)
 +
 +    def fw_version(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the major, minor and build versions of the firmware as 
 +        a 3-item list.
 +        &#​34;&#​34;&#​34; ​
 +        return self.bus.read_i2c_block_data(self.addr,​ 3, 3)
 +
 +    def resource_url(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the url of a JSON file that contains information about 
 +        resources, such as the location of the latest version of this code, 
 +        latest firmware, etc.
 +        &#​34;&#​34;&#​34;​
 +        chars = self.bus.read_i2c_block_data(self.addr,​ 6, 32)
 +        s = &#​39;&#​39;​
 +        for c in chars:
 +            if c==0: # read up to the null character
 +                break
 +            s += chr(c)
 +        return s
 +
 +    def uuid(self):
 +        &#​34;&#​34;&#​34;​
 +        Returns a 16-byte unique ID that can be used an a unique
 +        ID for your Vizy camera. ​ This unique ID is stored on the Vizy Power
 +        Board and remains constant regardless of firmware upgrades, etc.
 +        &#​34;&#​34;&#​34;​
 +        return self.bus.read_i2c_block_data(self.addr,​ 22, 16)
 +
 +    def power_off_requested(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns `True` if Vizy&#​39;​s button is held down for more than 5 seconds
 +        indicating that the user wishes to initiate safe shutdown and power
 +        off.  Returns `False` otherwise.
 +
 +        This is used by the vizy-powerd service. ​
 +        &#​34;&#​34;&#​34;​
 +        button = self.bus.read_i2c_block_data(self.addr,​ 38, 1)
 +        if button[0]==0x0f:​
 +            return True
 +        else:
 +            return False
 +
 +    def power_off(self,​ t=5000):
 +        &#​34;&#​34;&#​34;​
 +        Powers Vizy off. The `t` argument specifies how long 
 +        to wait before turning off (specified in milliseconds). ​ The 
 +        vizy-powerd service calls this upon shutdown.
 +        &#​34;&#​34;&#​34;​
 +        self.bus.write_i2c_block_data(self.addr,​ 38, [0x1f, int(t/​100)])
 +
 +    def power_on_alarm_date(self,​ datetime_=None):​
 +        &#​34;&#​34;&#​34;​
 +        If you wish to power off your Vizy and have it &#​34;​wake up&#34; at a
 +        specified time and date, call this method with the desired
 +        datetime object and initiate a shutdown. (e.g. `sudo shutdown now`).
 +
 +
 +        The code below tells Vizy to turn on on December 2, 2022, 11:18am.
 +
 +            import vizypowerboard as vpb
 +            from datetime import datetime
 +            v = vpb.VizyPowerBoard()
 +            d=datetime(year=2022,​ month=12, day=2, hour=11, minute=18, second=0)
 +            v.power_on_alarm_date(d)
 +
 +        Args:
 +          datetime_ (datetime, optional, default=None):​ `datetime` object that
 +            specifies the date/time to &#​34;​wake up&#34; (turn on).
 +
 +        Returns:
 +          Calling without a datetime object returns a `datetime` object ​
 +          reflecting the active alarm time.  If there is no active alarm,
 +          `None` is returned.
 +           
 +        Notes:
 +          * Once setting the alarm date, Vizy will retain it even if Vizy loses
 +          power for extended periods of time.
 +          * If the alarm expires while Vizy is on, Vizy will emit a buzzer tone
 +          and remain on.
 +          * If the alarm expires while Vizy is off (but plugged into and
 +          receiving power), Vizy will turn on.
 +          * If the alarm expires while Vizy is unplugged from (or not receiving)
 +          power, Vizy will turn on as soon as it receives power.  ​
 +        &#​34;&#​34;&#​34;​
 +        if datetime_ is None:
 +            t = self.bus.read_i2c_block_data(self.addr,​ 41, 6)
 +            if t[5]==0:
 +                return None
 +            return datetime.datetime(year=self._bcd2decimal(t[5])+2016,​ month=self._bcd2decimal(t[4]),​ day=self._bcd2decimal(t[3]),​ hour=self._bcd2decimal(t[2]),​ minute=self._bcd2decimal(t[1]),​ second=self._bcd2decimal(t[0]))
 +        t = [self._decimal2bcd(datetime_.second),​ self._decimal2bcd(datetime_.minute),​ self._decimal2bcd(datetime_.hour),​ self._decimal2bcd(datetime_.day),​ self._decimal2bcd(datetime_.month),​ self._decimal2bcd(datetime_.year-2016)]
 +        self.bus.write_i2c_block_data(self.addr,​ 41, t)
 +    ​
 +
 +    def power_on_alarm_seconds(self,​ seconds=None):​
 +        &#​34;&#​34;&#​34;​
 +        Allows you to specify a power on alarm in seconds in the future. ​
 +        For example, if you wish for Vizy to turn back on in 5 minutes, you
 +        would call `power_on_alarm_seconds(300)` and then initiate a shutdown.
 +        See `VizyPowerBoard.power_on_alarm_date()` for more information about the power on 
 +        alarm.
 +
 +        Args:
 +          seconds (integer, optional, default=None):​ Number of seconds in the
 +            future you wish Vizy to turn on in.
 +
 +        Returns:
 +          Calling this method without arguments returns the number of seconds
 +          until the alarm expires. ​ If no alarm is pending, `None` is returned.
 +        &#​34;&#​34;&#​34;​
 +        if seconds is None:
 +            pod = self.power_on_alarm_date()
 +            if pod is None:
 +                return None
 +            diff = pod - self.rtc()
 +            return diff.days*86400+diff.seconds ​
 +        # Add seconds to current time and set power on alarm    ​
 +        self.power_on_alarm_date(self.rtc()+datetime.timedelta(seconds=seconds))
 +
 +    def power_on_source(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns the source of what turned on Vizy for the current power cycle.
 +        It is one of either:
 +
 +        * POWER_ON_SOURCE_ALARM,​ indicates that Vizy was powered on
 +        by the power on alarm expiring. ​ See power_on_alarm_date() and
 +        power_on_alarm_seconds().
 +        * POWER_ON_SOURCE_POWER_BUTTON,​ indicates that Vizy was powered ​
 +        on by someone pressing the button.
 +        * POWER_ON_SOURCE_12V = indicates that Vizy was powered on
 +        by power being applied to 12V power input. ​ This only applies if the
 +        dip switch power mode allows powering on by plugging in power via the
 +        12V power input.
 +        * POWER_ON_SOURCE_5V,​ indicates that Vizy was powered on by applying
 +        power to the Raspberry Pi&#​39;​s USB-C power input.
 +        &#​34;&#​34;&#​34;​
 +        source = self.bus.read_i2c_block_data(self.addr,​ 40, 1)[0]
 +        return source
 +
 +    def button(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns `True` if the button is being pressed currently, `False` otherwise.
 +        &#​34;&#​34;&#​34;​
 +        button = self.bus.read_i2c_block_data(self.addr,​ 47, 1)
 +        if button[0]&​amp;​0x02:​
 +            return True
 +        else:
 +            return False
 +
 +    def button_pressed(self):​
 +        &#​34;&#​34;&#​34;​
 +        Returns `True` if the button was pressed within the last 5 seconds,
 +        `False` otherwise. ​ This is useful if the polling is intermittant or
 +        slow, as button presses are not missed (as long as you check at least
 +        every 5 seconds!)
 +        &#​34;&#​34;&#​34;​
 +        button = self.bus.read_i2c_block_data(self.addr,​ 47, 1)
 +        if button[0]&​amp;​0x01:​
 +            # Reset bit
 +            self.bus.write_i2c_block_data(self.addr,​ 47, [0])
 +            return True
 +        else:
 +            return False
 +
 +    def vcc12(self, state=None):​
 +        &#​34;&#​34;&#​34;​
 +        If `state` is `True`, the 12V output on Vizy&#​39;​s I/O connector (pin 2) will be enabled and output 12V.  If `state` is `False`, the 12V output
 +        will be disabled. ​ Calling without arguments returns its current state.
 +        &#​34;&#​34;&#​34; ​
 +        config = self.bus.read_i2c_block_data(self.addr,​ 48, 1)
 +        if state is None:
 +            return True if config&​amp;​0x01 else False
 +        if state:
 +            config[0] |= 0x01
 +        else:
 +            config[0] &amp;= ~0x01
 +
 +        self.bus.write_i2c_block_data(self.addr,​ 48, config)
 +
 +    def vcc5(self, state=None):​
 +        &#​34;&#​34;&#​34;​
 +        If `state` is `True`, the 5V output on Vizy&#​39;​s I/O connector (pin 3) will
 +        be enabled and output 5V.  If `state` is `False`, the 5V output will be
 +        disabled. ​ Calling without arguments returns its current state.
 +        &#​34;&#​34;&#​34; ​
 +        if state is None:
 +            return True if config&​amp;​0x02 else False
 +        if state:
 +            config[0] |= 0x02
 +        else:
 +            config[0] &amp;= ~0x02
 +
 +        self.bus.write_i2c_block_data(self.addr,​ 48, config)
 +
 +    def led(self, r=0, g=0, b=0, flashes=0, repeat=False,​ atten=255, on=100, off=100, pause=200):
 +        &#​34;&#​34;&#​34;​
 +        Controls the RGB LED in one of several modes:
 +
 +        * **Continuous**:​ just setting `r`, `g`, and `b` will set set the LED color
 +        and turn it on continuously. ​ `r`, `g`, and `b` values range between 0 
 +        and  255.
 +
 +                led(255, 0, 0)   # turn on LED, red color
 +                led(255, 255, 0) # turn on LED, yellow color
 +                led(0, 0, 255)   # turn on LED, blue color
 +                led(0, 0, 0)     # turn off LED
 +
 +        * **Flashes**:​ setting the `flashes` value to a non-zero value will
 +        cause the LED to flash the indicated number of times. ​ You can also
 +        specify the `on` and `off` arguments to indicate the amount of time 
 +        the LED is on and off for each flash (specified in milliseconds).
 +
 +                led(0, 0, 255, 3)  # flash blue 3 times (then stop)
 +                led(0, 0, 255, 3, on=500, off=500) ​ # flash blue 3 times, much more slowly
 +
 +        * **Repeated flashes**: setting the `repeat` argument to `True` ​
 +        will cause the indicated flash pattern to repeat forever. ​ You can
 +        modify the pause time between flash sequences by setting `pause`
 +        (milliseconds).
 +
 +                led(0, 0, 255, 3, True, pause=500) ​ # flash blue 3 times, pause, then repeat
 +                led(0, 0, 255, repeat=True,​ on=500, off=500)` ​ # flash blue forever
 +
 +        * **Flashing with attenuation**:​ you can also set the `atten` ​
 +        argument to make the LED to turn on and off slowly, like an
 +        incandescent light. ​ The value is the rate of change, so lower values
 +        cause the LED color to change more slowly. ​
 +
 +                led(0, 0, 255, repeat=True,​ atten=10, on=500, off=500) # flash blue forever, but turn on and turn off very slowly
 +        &#​34;&#​34;&#​34;​
 +        on = self._u_int8(on/​10)
 +        off = self._u_int8(off/​10)
 +        pause = self._u_int8(pause/​10)
 +        if flashes==0:
 +            mode = 0
 +        if repeat:
 +            mode = 0x02
 +        else:
 +            mode = 0x01
 +        self.bus.write_i2c_block_data(self.addr,​ 49, [mode, self._u_int8(r),​ self._u_int8(g),​ self._u_int8(b),​
 +            on, off, self._u_int8(flashes),​ pause, self._u_int8(atten)])
 +
 +    def led_unicorn(self,​ speed=10):
 +        &#​34;&#​34;&#​34;​
 +        This causes the LED to change color in succession: red, orange, yellow, ​
 +        green, cyan, blue, violet and then repeat again. ​ The `speed` argument ​
 +        ranges between 0 and 10.  For example, a `speed` of 0 causes the color 
 +        to change once every couple of seconds. ​ A `speed` of 10 causes the color to change about 6 times per second.
 +        &#​34;&#​34;&#​34;​
 +        if speed&​gt;​10:​
 +            speed = 10
 +        elif speed&​lt;​0:​
 +            speed = 0
 +
 +        on = self._u_int8(10 + (10-speed)*140/​10)
 +        atten = self._u_int8(3 + speed*47/​10) ​   ​
 +        self.bus.write_i2c_block_data(self.addr,​ 49, [0x08, 0, 0, 0, on, 0, 0, 0, atten])
 +
 +    def led_background(self,​ r=-1, g=-1, b=-1):
 +        &#​34;&#​34;&#​34;​
 +        The &#​34;​background&#​34;​ LED color is the color of the LED when the LED is 
 +        turned &#​34;​off&#​34;​. ​ It is used by system programs such as vizy-powerd to
 +        indicate Vizy&#​39;​s system state such as, booting (yellow), finished
 +        booting (green), running server (blue), etc.  Note, the background
 +        color does not influence the LED colors set by calls to led().
 +
 +        Calling led_background() without arguments returns the current ​
 +        background color r, g, and b values in a list. 
 +
 +            led_background(48,​ 48, 0)  # set background color to yellow
 +            led(0, 255, 0)  # turn on LED, green (as expected)
 +            led(0, 0, 0)  # turn LED off, and restore background color (yellow as set previously) ​   ​
 +        &#​34;&#​34;&#​34;​
 +        if r==-1:
 +            return self.bus.read_i2c_block_data(self.addr,​ 58, 3)
 +        self.bus.write_i2c_block_data(self.addr,​ 58, [self._u_int8(r),​ self._u_int8(g),​ self._u_int8(b)])
 + 
 +
 +    def buzzer(self,​ freq, on=250, off=250, count=1, shift=0):
 +        &#​34;&#​34;&#​34;​
 +        Emit tones through the buzzer. ​ The `freq` argument sets the frequency ​
 +        of the tone in Hz and the `on` argument sets the length of the tone in
 +        milliseconds.  ​
 +
 +        If you wish to emit more than 1 tone, you can set the
 +        `count` argument to the desired number.  ​
 +
 +        The `off` argument sets 
 +        the pause between tones in milliseconds. ​ The `shift` argument is a
 +        value ranging between -128 and 127 that causes the tone&#​39;​s frequency to
 +        raise if `shift` is greater than 0, or descend if `shift` is less 
 +        than 0.
 +
 +            buzzer(2000,​ 500) # emit a 2000Hz tone for 500ms
 +            buzzer(1000,​ count=3) # emit a 1000Hz tone 3 times   
 +            buzzer(1000,​ 500, 100, 3) # emit a longer 1000Hz tone 3 times
 +            buzzer(500, 250, 0, 10, 50) # emit 10 warbling tones like a siren
 +        &#​34;&#​34;&#​34;​
 +        freq = self._uint16(freq)
 +        f0 = freq&​amp;​0xff
 +        f1 = (freq&​gt;&​gt;​8)&​amp;​0xff
 +        self.bus.write_i2c_block_data(self.addr,​ 61, [0, f0, f1, self._u_int8(on/​10),​ self._u_int8(off/​10), ​
 +            self._u_int8(count),​ self._int8(shift)])
 +
 +
 +    def io_set_mode(self,​ bit, mode=None):
 +        &#​34;&#​34;&#​34;​
 +        Sets or gets the io mode of the given bit.  The `bit` argument ranges
 +        between ​ 0 and 3 and corresponds to pins 4 through 7 on Vizy&#​39;​s IO
 +        connector. Calling this method with no mode argument returns the mode
 +        of the given bit, otherwise, the `mode` argument can be one of 
 +        the following:
 +
 +        * IO_MODE_INPUT,​ sets the bit to high impedance input mode with 
 +        a weak pull-up resistor to 3.3V.  The input voltage can range between
 +        0 and Vin where Vin is the supply voltage. ​ Voltages lower than 1V 
 +        read as logic 0 via `VizyPowerBoard.io_bits()`. ​ Voltages higher ​
 +        than 1V are read as logic 1.
 +        * IO_MODE_OUTPUT,​ sets the bit to output mode.  If the bit is set to
 +        logic 0 via `VizyPowerBoard.io_bits()`,​ the output voltage is 0V.  ​
 +        If the bit is set to logic 1, the output voltage is 3.3V.  In this mode, each bit can source and sink 5mA.  ​
 +        * IO_MODE_HIGH_CURRENT,​ sets the bit to a special high current mode
 +        that allows the bit to sink as much as 1000mA, when the bit 
 +        set to logic 0 via `VizyPowerBoard.io_bits()`. ​ Otherwise, this mode
 +        behaves exactly as IO_MODE_OUTPUT. ​   ​
 +        &#​34;&#​34;&#​34;​
 +        if mode is None:
 +            return self.bus.read_i2c_block_data(self.addr,​ 68+bit, 1)[0]
 +        if bit==2:
 +            # set GPIO14 (UART TXD) as input so it doesn&#​39;​t conflict
 +            GPIO.setwarnings(False)
 +            GPIO.setmode(GPIO.BOARD)
 +            GPIO.setup(8,​ GPIO.IN)
 +        self.bus.write_i2c_block_data(self.addr,​ 68+bit, [self._u_int8(mode)])
 +
 +    def io_bits(self,​ bits=None):
 +        &#​34;&#​34;&#​34;​
 +        Sets or gets the logic state of the IO bits 0 through 3, corresponding
 +        to pins 4 through 7 on Vizy&#​39;​s IO connector. ​ The `bits` argument ranges
 +        between 0 and 15 as it is a binary representation of the logic state
 +        of the 4 IO bits.  If the `bits` argument isn&#​39;​t specified, the logic
 +        state of the 4 bits are returned.
 +
 +            io_bits(1) ​  # set IO bit 0 to logic 1 and bits 1, 2, 3 to logic 0
 +            io_bits(10) ​ # set IO bits 1 and 3 to logic 1 and bits 0 and 2 to logic 0
 +            bits = io_bits() ​ # get logic state of IO bits
 +            io_bits(io_bits()|1) ​ # set IO bit 0 to logic 1, leave bits 1, 2, 3 unchanged. ​
 +        &#​34;&#​34;&#​34;​
 +        if bits is None:
 +            return self.bus.read_i2c_block_data(self.addr,​ 72, 1)
 +        self.bus.write_i2c_block_data(self.addr,​ 72, [bits])
 +
 +
 +    def ir_filter(self,​ state=None, duration=None):​
 +        &#​34;&#​34;&#​34;​
 +        Actuates the electro-mechanical IR-cut filter on Vizy&#​39;​s camera. ​ Vizy
 +        uses a CMOS sensor which is very sensitive to IR light. ​ IR light can 
 +        adversely affect color fidelity during the daytime so an IR-cut filter
 +        is used to block the IR light (`state`=True). ​ During nighttime IR light
 +        is typically used as a discreet method of illumination and the IR-cut
 +        filter is removed (`state`=False). ​ If the `state` argument is `True`,
 +        the filter is actuated in place (and will stay there) until another
 +        call is made with the state argument set to `False` (in which case the
 +        IR-cut filter will be removed).  ​
 +
 +        The `duration` argument is optional and
 +        controls how long (in milliseconds) the actuation coil receives power.
 +
 +        Calling this method without arguments returns the state of IR-cut
 +        filter.
 +        &#​34;&#​34;&#​34;​
 +        if state is None:
 +            return True if self.bus.read_i2c_block_data(self.addr,​ 73, 1)[0] else False
 +        data = [1] if state else [0]
 +        if duration is not None:
 +            data.append(int(duration/​10))
 +        self.bus.write_i2c_block_data(self.addr,​ 73, data)
 +
 +    def fan(self, speed=None):​
 +        &#​34;&#​34;&#​34;​
 +        Set or get the fan speed. ​ The `speed` argument can range between 0 
 +        and 12 where 0 is off and 12 is maximum speed. ​ The fan speed 
 +        is typically regulated automatically by vizy-powerd.
 +
 +        Calling this method without arguments returns the current fan speed.  ​
 +        &#​34;&#​34;&#​34;​
 +        if speed is None:
 +            return self.bus.read_i2c_block_data(self.addr,​ 75, 1)[0]
 +        self.bus.write_i2c_block_data(self.addr,​ 75, [self._u_int8(speed)])
 +       
 +
 +    def rtc(self, datetime_=None):​
 +        &#​34;&#​34;&#​34;​
 +        Set or get the real-time clock time/​date. ​ The Vizy power board has a 
 +        battery-backed real-time clock that keeps track of time/date, power 
 +        alarms, etc. even while Vizy is receiving no power. ​ Passing in a
 +        datetime object sets the time/​date.  ​
 +
 +        Calling this method with no
 +        arguments returns a datetime object representing the current ​
 +        date/​time. ​
 +
 +        For example, the code below sets the date to December 2, 2020, 11:18am:
 +
 +            from datetime import datetime
 +            import vizypowerboard as vpb
 +            v = vpb.VizyPowerBoard()
 +            t = datetime(year=2020,​ month=12, day=2, hour=11, minute=18, second=0)
 +            v.rtc(t)
 +        &#​34;&#​34;&#​34;​
 +        if datetime_ is None:
 +            # Initiate RTC retrieval.
 +            self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC])
 +            # Wait until it&#​39;​s ready.
 +            self._wait_until_not_busy()
 +
 +            t = self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 8)
 +            return datetime.datetime(year=self._bcd2decimal(t[7])+2016,​ month=self._bcd2decimal(t[6]),​ day=self._bcd2decimal(t[4]),​ hour=self._bcd2decimal(t[3]),​ minute=self._bcd2decimal(t[2]),​ second=self._bcd2decimal(t[1]))
 + 
 +        t = [EXEC_RTC|EXEC_WRITE,​ 0, self._decimal2bcd(datetime_.second),​ self._decimal2bcd(datetime_.minute),​ self._decimal2bcd(datetime_.hour),​ self._decimal2bcd(datetime_.day),​ 0, self._decimal2bcd(datetime_.month),​ self._decimal2bcd(datetime_.year-2016)]
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ t)
 +        self._wait_until_not_busy()
 +
 +    def dip_switches(self,​ val=None):
 +        &#​34;&#​34;&#​34;​
 +        Set or get the (virtual) DIP switch state. ​ The DIP switches are a set 
 +        of &#​34;​switches&#​34;​ that allow you to control Vizy&#​39;​s power-on or power-off
 +        behavior. ​ Once they are set, they will retain the setting even if 
 +        power is removed. ​
 +        The switches are a set of values that can be ORed together:
 +
 +        * DIPSWITCH_EXT_BUTTON,​ used to set external/​remote power button, ​
 +        e.g. with outdoor enclosure. ​ Default disabled.
 +        * DIPSWITCH_MUTE_BUZZER,​ used to mute the buzzer. ​ Default disabled.
 +        * DIPSWITCH_NO_BG_LED,​ used to disable the background LED, which is 
 +        normally set to yellow upon power up.  Default disabled.
 +        * DIPSWITCH_POWER_DEFAULT_OFF,​ if this power mode is set and you 
 +        plug in power via the 12V power input, Vizy will remain off by default
 +        until you press the button to power Vizy on.  And if power is
 +        interrupted while Vizy is on, *Vizy will turn off*.  If power is
 +        interrupted while Vizy is off, Vizy will remain off.  This is the
 +        default power mode.
 +        * DIPSWITCH_POWER_DEFAULT_ON,​ if this power mode is set and you 
 +        plug in power via the 12V power input, Vizy will turn on by default
 +        without pressing the button. ​ And if power is interrupted while Vizy
 +        is on, Vizy will reset, but remain on.  If power is interrupted while
 +        Vizy is off, *Vizy will turn on*.  Default disabled.
 +        * DIPSWITCH_POWER_SWITCH,​ if this power mode is set and you plug in
 +        power via the 12V power input, Vizy will remain off (as in
 +        DIPSWITCH_POWER_DEFAULT_OFF mode), unless power was 
 +        removed while Vizy was on.  In this case Vizy will turn on when you
 +        (re)apply power. ​ If power is interrupted while Vizy is off, Vizy 
 +        will remain off.  This behavior is similar to the behavior of a real
 +        power switch in that it retains the power &#​34;​state&#​34;​ (on or off) and acts
 +        accordingly. ​ Default disabled.
 +        * DIPSWITCH_POWER_PLUG,​ if this power mode is set Vizy will remain
 +        powered on as long as it receives power through the 12V power plug, 
 +        and you will not be able to turn off Vizy via button or software as
 +        long as it&#​39;​s plugged in and receiving power. ​ Default disabled.
 +        ​
 +        For example: ​
 +
 +            dip_switches(DIPSWITCH_EXT_BUTTON | DIPSWITCH_POWER_SWITCH) # set external power button and power switch mode
 +        &#​34;&#​34;&#​34;​
 +        if val is None:
 +            self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_NVCONFIG])
 +            # Wait until it&#​39;​s ready.
 +            self._wait_until_not_busy()
 +            return self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 1)[0]
 +
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_NVCONFIG|EXEC_WRITE,​ self._u_int8(val)])
 +        self._wait_until_not_busy()
 +
 +    def rtc_adjust(self,​ val=None):
 +        &#​34;&#​34;&#​34;​
 +        Set or get the real-time clock adjustment. ​ Vizy&#​39;​s real-time clock 
 +        crystal has an accuracy of 20ppm, which means that it can lose or gain 
 +        up to 20 seconds for every 1 million elapsed seconds. ​ Normally, this
 +        isn&#​39;​t an issue, but if Vizy spends a lengthy period of time (months)
 +        without Internet access, it could lose or gain minutes, which 
 +        depending on the application could be significant. The adjustment
 +        value can offset this inaccuracy. ​ The `val` argument can range 
 +        between -128 and 127 and has a multiplier of 2.170 ppm.  ​
 +
 +        For example, ​
 +        if the RTC is gaining 10 seconds every 1 million seconds, you would 
 +        call `rtc_adjust(-5)`. ​ If the RTC is losing 10 seconds every million
 +        seconds you would call `rtc_adjust(5)`.
 +        ​
 +        The adjustment value is retained by the real-time clock even when 
 +        Vizy&#​39;​s power is removed. ​
 +        &#​34;&#​34;&#​34;​
 +        if val is None:
 +            self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC_CALIBRATE])
 +            # Wait until it&#​39;​s ready.
 +            self._wait_until_not_busy()
 +            return self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 1)[0]
 +
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC_CALIBRATE|EXEC_WRITE,​ self._int8(val)])
 +        self._wait_until_not_busy()
 +
 +
 +    def measure(self,​ channel):
 +        &#​34;&#​34;&#​34;​
 +        Get the voltage values of various channels. ​ The returned value is 
 +        the voltage measured (in Volts) of the given channel. ​ The `channel`
 +        argument can be one of the following:
 +
 +        * CHANNEL_VIN,​ this channel measures the voltage present at the 12V
 +        power input.
 +        * CHANNEL_5V, this channel measures the voltage present at the 5V
 +        voltage rail provided to the Raspberry Pi.
 +        &#​34;&#​34;&#​34;  ​
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_AD, self._u_int8(channel)])
 +        # Wait until it&#​39;​s ready.
 +        self._wait_until_not_busy()
 +        val = self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+2,​ 2)
 +        return (val[1]*0x100 + val[0])/​1000
 +
 +
 +    def rtc_set_system_datetime(self,​ datetime_=None):​
 +        &#​34;&#​34;&#​34;​
 +        A convenience method that sets the system time/date based on the 
 +        real-time time/​date. ​ This is called by vizy-powerd upon power-up. ​
 +        &#​34;&#​34;&#​34;​
 +        if os.geteuid()!=0:​
 +            raise PermissionError(&#​34;​You need root permission to set the time/​date.&#​34;​)
 +        if datetime_ is None:
 +            datetime_ = self.rtc()
 +        s = datetime_.isoformat()
 +        os.system(f&#​34;​sudo date -s {s}&#​34;​)</​code></​pre>​
 +</​details>​
 +<​h3>​Methods</​h3>​
 +<dl>
 +<dt id="​vizypowerboard.VizyPowerBoard.button"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​button</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns <​code>​True</​code>​ if the button is being pressed currently, <​code>​False</​code>​ otherwise.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def button(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns `True` if the button is being pressed currently, `False` otherwise.
 +    &#​34;&#​34;&#​34;​
 +    button = self.bus.read_i2c_block_data(self.addr,​ 47, 1)
 +    if button[0]&​amp;​0x02:​
 +        return True
 +    else:
 +        return False</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.button_pressed"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​button_pressed</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns <​code>​True</​code>​ if the button was pressed within the last 5 seconds,
 +<​code>​False</​code>​ otherwise.
 +This is useful if the polling is intermittant or
 +slow, as button presses are not missed (as long as you check at least
 +every 5 seconds!)</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def button_pressed(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns `True` if the button was pressed within the last 5 seconds,
 +    `False` otherwise. ​ This is useful if the polling is intermittant or
 +    slow, as button presses are not missed (as long as you check at least
 +    every 5 seconds!)
 +    &#​34;&#​34;&#​34;​
 +    button = self.bus.read_i2c_block_data(self.addr,​ 47, 1)
 +    if button[0]&​amp;​0x01:​
 +        # Reset bit
 +        self.bus.write_i2c_block_data(self.addr,​ 47, [0])
 +        return True
 +    else:
 +        return False</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.buzzer"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​buzzer</​span></​span>​(<​span>​self,​ freq, on=250, off=250, count=1, shift=0)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Emit tones through the buzzer.
 +The <​code>​freq</​code>​ argument sets the frequency
 +of the tone in Hz and the <​code>​on</​code>​ argument sets the length of the tone in
 +milliseconds.
 +</p>
 +<p>If you wish to emit more than 1 tone, you can set the
 +<​code>​count</​code>​ argument to the desired number.
 +</p>
 +<​p>​The <​code>​off</​code>​ argument sets
 +the pause between tones in milliseconds.
 +The <​code>​shift</​code>​ argument is a
 +value ranging between -128 and 127 that causes the tone's frequency to
 +raise if <​code>​shift</​code>​ is greater than 0, or descend if <​code>​shift</​code>​ is less
 +than 0.</​p>​
 +<​pre><​code>​buzzer(2000,​ 500) # emit a 2000Hz tone for 500ms
 +buzzer(1000,​ count=3) # emit a 1000Hz tone 3 times   
 +buzzer(1000,​ 500, 100, 3) # emit a longer 1000Hz tone 3 times
 +buzzer(500, 250, 0, 10, 50) # emit 10 warbling tones like a siren
 +</​code></​pre></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def buzzer(self,​ freq, on=250, off=250, count=1, shift=0):
 +    &#​34;&#​34;&#​34;​
 +    Emit tones through the buzzer. ​ The `freq` argument sets the frequency ​
 +    of the tone in Hz and the `on` argument sets the length of the tone in
 +    milliseconds.  ​
 +
 +    If you wish to emit more than 1 tone, you can set the
 +    `count` argument to the desired number.  ​
 +
 +    The `off` argument sets 
 +    the pause between tones in milliseconds. ​ The `shift` argument is a
 +    value ranging between -128 and 127 that causes the tone&#​39;​s frequency to
 +    raise if `shift` is greater than 0, or descend if `shift` is less 
 +    than 0.
 +
 +        buzzer(2000,​ 500) # emit a 2000Hz tone for 500ms
 +        buzzer(1000,​ count=3) # emit a 1000Hz tone 3 times   
 +        buzzer(1000,​ 500, 100, 3) # emit a longer 1000Hz tone 3 times
 +        buzzer(500, 250, 0, 10, 50) # emit 10 warbling tones like a siren
 +    &#​34;&#​34;&#​34;​
 +    freq = self._uint16(freq)
 +    f0 = freq&​amp;​0xff
 +    f1 = (freq&​gt;&​gt;​8)&​amp;​0xff
 +    self.bus.write_i2c_block_data(self.addr,​ 61, [0, f0, f1, self._u_int8(on/​10),​ self._u_int8(off/​10), ​
 +        self._u_int8(count),​ self._int8(shift)])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.dip_switches"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​dip_switches</​span></​span>​(<​span>​self,​ val=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Set or get the (virtual) DIP switch state.
 +The DIP switches are a set
 +of "​switches"​ that allow you to control Vizy's power-on or power-off
 +behavior.
 +Once they are set, they will retain the setting even if
 +power is removed.
 +The switches are a set of values that can be ORed together:</​p>​
 +<ul>
 +<​li>​DIPSWITCH_EXT_BUTTON,​ used to set external/​remote power button,
 +e.g. with outdoor enclosure.
 +Default disabled.</​li>​
 +<​li>​DIPSWITCH_MUTE_BUZZER,​ used to mute the buzzer.
 +Default disabled.</​li>​
 +<​li>​DIPSWITCH_NO_BG_LED,​ used to disable the background LED, which is
 +normally set to yellow upon power up.
 +Default disabled.</​li>​
 +<​li>​DIPSWITCH_POWER_DEFAULT_OFF,​ if this power mode is set and you
 +plug in power via the 12V power input, Vizy will remain off by default
 +until you press the button to power Vizy on.
 +And if power is
 +interrupted while Vizy is on, <​em>​Vizy will turn off</​em>​.
 +If power is
 +interrupted while Vizy is off, Vizy will remain off.
 +This is the
 +default power mode.</​li>​
 +<​li>​DIPSWITCH_POWER_DEFAULT_ON,​ if this power mode is set and you
 +plug in power via the 12V power input, Vizy will turn on by default
 +without pressing the button.
 +And if power is interrupted while Vizy
 +is on, Vizy will reset, but remain on.
 +If power is interrupted while
 +Vizy is off, <​em>​Vizy will turn on</​em>​.
 +Default disabled.</​li>​
 +<​li>​DIPSWITCH_POWER_SWITCH,​ if this power mode is set and you plug in
 +power via the 12V power input, Vizy will remain off (as in
 +DIPSWITCH_POWER_DEFAULT_OFF mode), unless power was
 +removed while Vizy was on.
 +In this case Vizy will turn on when you
 +(re)apply power.
 +If power is interrupted while Vizy is off, Vizy
 +will remain off.
 +This behavior is similar to the behavior of a real
 +power switch in that it retains the power "​state"​ (on or off) and acts
 +accordingly.
 +Default disabled.</​li>​
 +<​li>​DIPSWITCH_POWER_PLUG,​ if this power mode is set Vizy will remain
 +powered on as long as it receives power through the 12V power plug,
 +and you will not be able to turn off Vizy via button or software as
 +long as it's plugged in and receiving power.
 +Default disabled.</​li>​
 +</ul>
 +<​p>​For example: </p>
 +<​pre><​code>​dip_switches(DIPSWITCH_EXT_BUTTON | DIPSWITCH_POWER_SWITCH) # set external power button and power switch mode
 +</​code></​pre></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def dip_switches(self,​ val=None):
 +    &#​34;&#​34;&#​34;​
 +    Set or get the (virtual) DIP switch state. ​ The DIP switches are a set 
 +    of &#​34;​switches&#​34;​ that allow you to control Vizy&#​39;​s power-on or power-off
 +    behavior. ​ Once they are set, they will retain the setting even if 
 +    power is removed. ​
 +    The switches are a set of values that can be ORed together:
 +
 +    * DIPSWITCH_EXT_BUTTON,​ used to set external/​remote power button, ​
 +    e.g. with outdoor enclosure. ​ Default disabled.
 +    * DIPSWITCH_MUTE_BUZZER,​ used to mute the buzzer. ​ Default disabled.
 +    * DIPSWITCH_NO_BG_LED,​ used to disable the background LED, which is 
 +    normally set to yellow upon power up.  Default disabled.
 +    * DIPSWITCH_POWER_DEFAULT_OFF,​ if this power mode is set and you 
 +    plug in power via the 12V power input, Vizy will remain off by default
 +    until you press the button to power Vizy on.  And if power is
 +    interrupted while Vizy is on, *Vizy will turn off*.  If power is
 +    interrupted while Vizy is off, Vizy will remain off.  This is the
 +    default power mode.
 +    * DIPSWITCH_POWER_DEFAULT_ON,​ if this power mode is set and you 
 +    plug in power via the 12V power input, Vizy will turn on by default
 +    without pressing the button. ​ And if power is interrupted while Vizy
 +    is on, Vizy will reset, but remain on.  If power is interrupted while
 +    Vizy is off, *Vizy will turn on*.  Default disabled.
 +    * DIPSWITCH_POWER_SWITCH,​ if this power mode is set and you plug in
 +    power via the 12V power input, Vizy will remain off (as in
 +    DIPSWITCH_POWER_DEFAULT_OFF mode), unless power was 
 +    removed while Vizy was on.  In this case Vizy will turn on when you
 +    (re)apply power. ​ If power is interrupted while Vizy is off, Vizy 
 +    will remain off.  This behavior is similar to the behavior of a real
 +    power switch in that it retains the power &#​34;​state&#​34;​ (on or off) and acts
 +    accordingly. ​ Default disabled.
 +    * DIPSWITCH_POWER_PLUG,​ if this power mode is set Vizy will remain
 +    powered on as long as it receives power through the 12V power plug, 
 +    and you will not be able to turn off Vizy via button or software as
 +    long as it&#​39;​s plugged in and receiving power. ​ Default disabled.
 +    ​
 +    For example: ​
 +
 +        dip_switches(DIPSWITCH_EXT_BUTTON | DIPSWITCH_POWER_SWITCH) # set external power button and power switch mode
 +    &#​34;&#​34;&#​34;​
 +    if val is None:
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_NVCONFIG])
 +        # Wait until it&#​39;​s ready.
 +        self._wait_until_not_busy()
 +        return self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 1)[0]
 +
 +    self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_NVCONFIG|EXEC_WRITE,​ self._u_int8(val)])
 +    self._wait_until_not_busy()</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.fan"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​fan</​span></​span>​(<​span>​self,​ speed=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Set or get the fan speed.
 +The <​code>​speed</​code>​ argument can range between 0
 +and 12 where 0 is off and 12 is maximum speed.
 +The fan speed
 +is typically regulated automatically by vizy-powerd.</​p>​
 +<​p>​Calling this method without arguments returns the current fan speed.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def fan(self, speed=None):​
 +    &#​34;&#​34;&#​34;​
 +    Set or get the fan speed. ​ The `speed` argument can range between 0 
 +    and 12 where 0 is off and 12 is maximum speed. ​ The fan speed 
 +    is typically regulated automatically by vizy-powerd.
 +
 +    Calling this method without arguments returns the current fan speed.  ​
 +    &#​34;&#​34;&#​34;​
 +    if speed is None:
 +        return self.bus.read_i2c_block_data(self.addr,​ 75, 1)[0]
 +    self.bus.write_i2c_block_data(self.addr,​ 75, [self._u_int8(speed)])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.fw_version"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​fw_version</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns the major, minor and build versions of the firmware as
 +a 3-item list.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def fw_version(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns the major, minor and build versions of the firmware as 
 +    a 3-item list.
 +    &#​34;&#​34;&#​34; ​
 +    return self.bus.read_i2c_block_data(self.addr,​ 3, 3)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.hw_version"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​hw_version</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns the major and minor versions of the PCB as a 2-item list.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def hw_version(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns the major and minor versions of the PCB as a 2-item list.
 +    &#​34;&#​34;&#​34; ​
 +    return self.bus.read_i2c_block_data(self.addr,​ 1, 2)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.io_bits"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​io_bits</​span></​span>​(<​span>​self,​ bits=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Sets or gets the logic state of the IO bits 0 through 3, corresponding
 +to pins 4 through 7 on Vizy's IO connector.
 +The <​code>​bits</​code>​ argument ranges
 +between 0 and 15 as it is a binary representation of the logic state
 +of the 4 IO bits.
 +If the <​code>​bits</​code>​ argument isn't specified, the logic
 +state of the 4 bits are returned.</​p>​
 +<​pre><​code>​io_bits(1) ​  # set IO bit 0 to logic 1 and bits 1, 2, 3 to logic 0
 +io_bits(10) ​ # set IO bits 1 and 3 to logic 1 and bits 0 and 2 to logic 0
 +bits = io_bits() ​ # get logic state of IO bits
 +io_bits(io_bits()|1) ​ # set IO bit 0 to logic 1, leave bits 1, 2, 3 unchanged.
 +</​code></​pre></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def io_bits(self,​ bits=None):
 +    &#​34;&#​34;&#​34;​
 +    Sets or gets the logic state of the IO bits 0 through 3, corresponding
 +    to pins 4 through 7 on Vizy&#​39;​s IO connector. ​ The `bits` argument ranges
 +    between 0 and 15 as it is a binary representation of the logic state
 +    of the 4 IO bits.  If the `bits` argument isn&#​39;​t specified, the logic
 +    state of the 4 bits are returned.
 +
 +        io_bits(1) ​  # set IO bit 0 to logic 1 and bits 1, 2, 3 to logic 0
 +        io_bits(10) ​ # set IO bits 1 and 3 to logic 1 and bits 0 and 2 to logic 0
 +        bits = io_bits() ​ # get logic state of IO bits
 +        io_bits(io_bits()|1) ​ # set IO bit 0 to logic 1, leave bits 1, 2, 3 unchanged. ​
 +    &#​34;&#​34;&#​34;​
 +    if bits is None:
 +        return self.bus.read_i2c_block_data(self.addr,​ 72, 1)
 +    self.bus.write_i2c_block_data(self.addr,​ 72, [bits])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.io_set_mode"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​io_set_mode</​span></​span>​(<​span>​self,​ bit, mode=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Sets or gets the io mode of the given bit.
 +The <​code>​bit</​code>​ argument ranges
 +between
 +0 and 3 and corresponds to pins 4 through 7 on Vizy's IO
 +connector. Calling this method with no mode argument returns the mode
 +of the given bit, otherwise, the <​code>​mode</​code>​ argument can be one of
 +the following:</​p>​
 +<ul>
 +<​li>​IO_MODE_INPUT,​ sets the bit to high impedance input mode with
 +a weak pull-up resistor to 3.3V.
 +The input voltage can range between
 +0 and Vin where Vin is the supply voltage.
 +Voltages lower than 1V
 +read as logic 0 via <​code><​a title="​vizypowerboard.VizyPowerBoard.io_bits"​ href="#​vizypowerboard.VizyPowerBoard.io_bits">​VizyPowerBoard.io_bits()</​a></​code>​.
 +Voltages higher
 +than 1V are read as logic 1.</​li>​
 +<​li>​IO_MODE_OUTPUT,​ sets the bit to output mode.
 +If the bit is set to
 +logic 0 via <​code><​a title="​vizypowerboard.VizyPowerBoard.io_bits"​ href="#​vizypowerboard.VizyPowerBoard.io_bits">​VizyPowerBoard.io_bits()</​a></​code>,​ the output voltage is 0V.<​br>​
 +If the bit is set to logic 1, the output voltage is 3.3V.
 +In this mode, each bit can source and sink 5mA.
 +</li>
 +<​li>​IO_MODE_HIGH_CURRENT,​ sets the bit to a special high current mode
 +that allows the bit to sink as much as 1000mA, when the bit
 +set to logic 0 via <​code><​a title="​vizypowerboard.VizyPowerBoard.io_bits"​ href="#​vizypowerboard.VizyPowerBoard.io_bits">​VizyPowerBoard.io_bits()</​a></​code>​.
 +Otherwise, this mode
 +behaves exactly as IO_MODE_OUTPUT.</​li>​
 +</​ul></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def io_set_mode(self,​ bit, mode=None):
 +    &#​34;&#​34;&#​34;​
 +    Sets or gets the io mode of the given bit.  The `bit` argument ranges
 +    between ​ 0 and 3 and corresponds to pins 4 through 7 on Vizy&#​39;​s IO
 +    connector. Calling this method with no mode argument returns the mode
 +    of the given bit, otherwise, the `mode` argument can be one of 
 +    the following:
 +
 +    * IO_MODE_INPUT,​ sets the bit to high impedance input mode with 
 +    a weak pull-up resistor to 3.3V.  The input voltage can range between
 +    0 and Vin where Vin is the supply voltage. ​ Voltages lower than 1V 
 +    read as logic 0 via `VizyPowerBoard.io_bits()`. ​ Voltages higher ​
 +    than 1V are read as logic 1.
 +    * IO_MODE_OUTPUT,​ sets the bit to output mode.  If the bit is set to
 +    logic 0 via `VizyPowerBoard.io_bits()`,​ the output voltage is 0V.  ​
 +    If the bit is set to logic 1, the output voltage is 3.3V.  In this mode, each bit can source and sink 5mA.  ​
 +    * IO_MODE_HIGH_CURRENT,​ sets the bit to a special high current mode
 +    that allows the bit to sink as much as 1000mA, when the bit 
 +    set to logic 0 via `VizyPowerBoard.io_bits()`. ​ Otherwise, this mode
 +    behaves exactly as IO_MODE_OUTPUT. ​   ​
 +    &#​34;&#​34;&#​34;​
 +    if mode is None:
 +        return self.bus.read_i2c_block_data(self.addr,​ 68+bit, 1)[0]
 +    if bit==2:
 +        # set GPIO14 (UART TXD) as input so it doesn&#​39;​t conflict
 +        GPIO.setwarnings(False)
 +        GPIO.setmode(GPIO.BOARD)
 +        GPIO.setup(8,​ GPIO.IN)
 +    self.bus.write_i2c_block_data(self.addr,​ 68+bit, [self._u_int8(mode)])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.ir_filter"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​ir_filter</​span></​span>​(<​span>​self,​ state=None, duration=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Actuates the electro-mechanical IR-cut filter on Vizy's camera.
 +Vizy
 +uses a CMOS sensor which is very sensitive to IR light.
 +IR light can
 +adversely affect color fidelity during the daytime so an IR-cut filter
 +is used to block the IR light (<​code>​state</​code>​=True).
 +During nighttime IR light
 +is typically used as a discreet method of illumination and the IR-cut
 +filter is removed (<​code>​state</​code>​=False).
 +If the <​code>​state</​code>​ argument is <​code>​True</​code>,​
 +the filter is actuated in place (and will stay there) until another
 +call is made with the state argument set to <​code>​False</​code>​ (in which case the
 +IR-cut filter will be removed).
 +</p>
 +<​p>​The <​code>​duration</​code>​ argument is optional and
 +controls how long (in milliseconds) the actuation coil receives power.</​p>​
 +<​p>​Calling this method without arguments returns the state of IR-cut
 +filter.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def ir_filter(self,​ state=None, duration=None):​
 +    &#​34;&#​34;&#​34;​
 +    Actuates the electro-mechanical IR-cut filter on Vizy&#​39;​s camera. ​ Vizy
 +    uses a CMOS sensor which is very sensitive to IR light. ​ IR light can 
 +    adversely affect color fidelity during the daytime so an IR-cut filter
 +    is used to block the IR light (`state`=True). ​ During nighttime IR light
 +    is typically used as a discreet method of illumination and the IR-cut
 +    filter is removed (`state`=False). ​ If the `state` argument is `True`,
 +    the filter is actuated in place (and will stay there) until another
 +    call is made with the state argument set to `False` (in which case the
 +    IR-cut filter will be removed).  ​
 +
 +    The `duration` argument is optional and
 +    controls how long (in milliseconds) the actuation coil receives power.
 +
 +    Calling this method without arguments returns the state of IR-cut
 +    filter.
 +    &#​34;&#​34;&#​34;​
 +    if state is None:
 +        return True if self.bus.read_i2c_block_data(self.addr,​ 73, 1)[0] else False
 +    data = [1] if state else [0]
 +    if duration is not None:
 +        data.append(int(duration/​10))
 +    self.bus.write_i2c_block_data(self.addr,​ 73, data)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.led"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​led</​span></​span>​(<​span>​self,​ r=0, g=0, b=0, flashes=0, repeat=False,​ atten=255, on=100, off=100, pause=200)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Controls the RGB LED in one of several modes:</​p>​
 +<ul>
 +<li>
 +<​p><​strong>​Continuous</​strong>:​ just setting <​code>​r</​code>,​ <​code>​g</​code>,​ and <​code>​b</​code>​ will set set the LED color
 +and turn it on continuously.
 +<​code>​r</​code>,​ <​code>​g</​code>,​ and <​code>​b</​code>​ values range between 0
 +and
 +255.</​p>​
 +<​pre><​code>​led(255,​ 0, 0)   # turn on LED, red color
 +led(255, 255, 0) # turn on LED, yellow color
 +led(0, 0, 255)   # turn on LED, blue color
 +led(0, 0, 0)     # turn off LED
 +</​code></​pre>​
 +</li>
 +<li>
 +<​p><​strong>​Flashes</​strong>:​ setting the <​code>​flashes</​code>​ value to a non-zero value will
 +cause the LED to flash the indicated number of times.
 +You can also
 +specify the <​code>​on</​code>​ and <​code>​off</​code>​ arguments to indicate the amount of time
 +the LED is on and off for each flash (specified in milliseconds).</​p>​
 +<​pre><​code>​led(0,​ 0, 255, 3)  # flash blue 3 times (then stop)
 +led(0, 0, 255, 3, on=500, off=500) ​ # flash blue 3 times, much more slowly
 +</​code></​pre>​
 +</li>
 +<li>
 +<​p><​strong>​Repeated flashes</​strong>:​ setting the <​code>​repeat</​code>​ argument to <​code>​True</​code>​
 +will cause the indicated flash pattern to repeat forever.
 +You can
 +modify the pause time between flash sequences by setting <​code>​pause</​code>​
 +(milliseconds).</​p>​
 +<​pre><​code>​led(0,​ 0, 255, 3, True, pause=500) ​ # flash blue 3 times, pause, then repeat
 +led(0, 0, 255, repeat=True,​ on=500, off=500)` ​ # flash blue forever
 +</​code></​pre>​
 +</li>
 +<li>
 +<​p><​strong>​Flashing with attenuation</​strong>:​ you can also set the <​code>​atten</​code>​
 +argument to make the LED to turn on and off slowly, like an
 +incandescent light.
 +The value is the rate of change, so lower values
 +cause the LED color to change more slowly. </p>
 +<​pre><​code>​led(0,​ 0, 255, repeat=True,​ atten=10, on=500, off=500) # flash blue forever, but turn on and turn off very slowly
 +</​code></​pre>​
 +</li>
 +</​ul></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def led(self, r=0, g=0, b=0, flashes=0, repeat=False,​ atten=255, on=100, off=100, pause=200):
 +    &#​34;&#​34;&#​34;​
 +    Controls the RGB LED in one of several modes:
 +
 +    * **Continuous**:​ just setting `r`, `g`, and `b` will set set the LED color
 +    and turn it on continuously. ​ `r`, `g`, and `b` values range between 0 
 +    and  255.
 +
 +            led(255, 0, 0)   # turn on LED, red color
 +            led(255, 255, 0) # turn on LED, yellow color
 +            led(0, 0, 255)   # turn on LED, blue color
 +            led(0, 0, 0)     # turn off LED
 +
 +    * **Flashes**:​ setting the `flashes` value to a non-zero value will
 +    cause the LED to flash the indicated number of times. ​ You can also
 +    specify the `on` and `off` arguments to indicate the amount of time 
 +    the LED is on and off for each flash (specified in milliseconds).
 +
 +            led(0, 0, 255, 3)  # flash blue 3 times (then stop)
 +            led(0, 0, 255, 3, on=500, off=500) ​ # flash blue 3 times, much more slowly
 +
 +    * **Repeated flashes**: setting the `repeat` argument to `True` ​
 +    will cause the indicated flash pattern to repeat forever. ​ You can
 +    modify the pause time between flash sequences by setting `pause`
 +    (milliseconds).
 +
 +            led(0, 0, 255, 3, True, pause=500) ​ # flash blue 3 times, pause, then repeat
 +            led(0, 0, 255, repeat=True,​ on=500, off=500)` ​ # flash blue forever
 +
 +    * **Flashing with attenuation**:​ you can also set the `atten` ​
 +    argument to make the LED to turn on and off slowly, like an
 +    incandescent light. ​ The value is the rate of change, so lower values
 +    cause the LED color to change more slowly. ​
 +
 +            led(0, 0, 255, repeat=True,​ atten=10, on=500, off=500) # flash blue forever, but turn on and turn off very slowly
 +    &#​34;&#​34;&#​34;​
 +    on = self._u_int8(on/​10)
 +    off = self._u_int8(off/​10)
 +    pause = self._u_int8(pause/​10)
 +    if flashes==0:
 +        mode = 0
 +    if repeat:
 +        mode = 0x02
 +    else:
 +        mode = 0x01
 +    self.bus.write_i2c_block_data(self.addr,​ 49, [mode, self._u_int8(r),​ self._u_int8(g),​ self._u_int8(b),​
 +        on, off, self._u_int8(flashes),​ pause, self._u_int8(atten)])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.led_background"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​led_background</​span></​span>​(<​span>​self,​ r=-1, g=-1, b=-1)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​The "​background"​ LED color is the color of the LED when the LED is
 +turned "​off"​.
 +It is used by system programs such as vizy-powerd to
 +indicate Vizy's system state such as, booting (yellow), finished
 +booting (green), running server (blue), etc.
 +Note, the background
 +color does not influence the LED colors set by calls to led().</​p>​
 +<​p>​Calling led_background() without arguments returns the current
 +background color r, g, and b values in a list. </p>
 +<​pre><​code>​led_background(48,​ 48, 0)  # set background color to yellow
 +led(0, 255, 0)  # turn on LED, green (as expected)
 +led(0, 0, 0)  # turn LED off, and restore background color (yellow as set previously)
 +</​code></​pre></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def led_background(self,​ r=-1, g=-1, b=-1):
 +    &#​34;&#​34;&#​34;​
 +    The &#​34;​background&#​34;​ LED color is the color of the LED when the LED is 
 +    turned &#​34;​off&#​34;​. ​ It is used by system programs such as vizy-powerd to
 +    indicate Vizy&#​39;​s system state such as, booting (yellow), finished
 +    booting (green), running server (blue), etc.  Note, the background
 +    color does not influence the LED colors set by calls to led().
 +
 +    Calling led_background() without arguments returns the current ​
 +    background color r, g, and b values in a list. 
 +
 +        led_background(48,​ 48, 0)  # set background color to yellow
 +        led(0, 255, 0)  # turn on LED, green (as expected)
 +        led(0, 0, 0)  # turn LED off, and restore background color (yellow as set previously) ​   ​
 +    &#​34;&#​34;&#​34;​
 +    if r==-1:
 +        return self.bus.read_i2c_block_data(self.addr,​ 58, 3)
 +    self.bus.write_i2c_block_data(self.addr,​ 58, [self._u_int8(r),​ self._u_int8(g),​ self._u_int8(b)])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.led_unicorn"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​led_unicorn</​span></​span>​(<​span>​self,​ speed=10)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​This causes the LED to change color in succession: red, orange, yellow,
 +green, cyan, blue, violet and then repeat again.
 +The <​code>​speed</​code>​ argument
 +ranges between 0 and 10.
 +For example, a <​code>​speed</​code>​ of 0 causes the color
 +to change once every couple of seconds.
 +A <​code>​speed</​code>​ of 10 causes the color to change about 6 times per second.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def led_unicorn(self,​ speed=10):
 +    &#​34;&#​34;&#​34;​
 +    This causes the LED to change color in succession: red, orange, yellow, ​
 +    green, cyan, blue, violet and then repeat again. ​ The `speed` argument ​
 +    ranges between 0 and 10.  For example, a `speed` of 0 causes the color 
 +    to change once every couple of seconds. ​ A `speed` of 10 causes the color to change about 6 times per second.
 +    &#​34;&#​34;&#​34;​
 +    if speed&​gt;​10:​
 +        speed = 10
 +    elif speed&​lt;​0:​
 +        speed = 0
 +
 +    on = self._u_int8(10 + (10-speed)*140/​10)
 +    atten = self._u_int8(3 + speed*47/​10) ​   ​
 +    self.bus.write_i2c_block_data(self.addr,​ 49, [0x08, 0, 0, 0, on, 0, 0, 0, atten])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.measure"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​measure</​span></​span>​(<​span>​self,​ channel)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Get the voltage values of various channels.
 +The returned value is
 +the voltage measured (in Volts) of the given channel.
 +The <​code>​channel</​code>​
 +argument can be one of the following:</​p>​
 +<ul>
 +<​li>​CHANNEL_VIN,​ this channel measures the voltage present at the 12V
 +power input.</​li>​
 +<​li>​CHANNEL_5V,​ this channel measures the voltage present at the 5V
 +voltage rail provided to the Raspberry Pi.</​li>​
 +</​ul></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def measure(self,​ channel):
 +    &#​34;&#​34;&#​34;​
 +    Get the voltage values of various channels. ​ The returned value is 
 +    the voltage measured (in Volts) of the given channel. ​ The `channel`
 +    argument can be one of the following:
 +
 +    * CHANNEL_VIN,​ this channel measures the voltage present at the 12V
 +    power input.
 +    * CHANNEL_5V, this channel measures the voltage present at the 5V
 +    voltage rail provided to the Raspberry Pi.
 +    &#​34;&#​34;&#​34;  ​
 +    self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_AD, self._u_int8(channel)])
 +    # Wait until it&#​39;​s ready.
 +    self._wait_until_not_busy()
 +    val = self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+2,​ 2)
 +    return (val[1]*0x100 + val[0])/​1000</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.power_off"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​power_off</​span></​span>​(<​span>​self,​ t=5000)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Powers Vizy off. The <​code>​t</​code>​ argument specifies how long
 +to wait before turning off (specified in milliseconds).
 +The
 +vizy-powerd service calls this upon shutdown.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def power_off(self,​ t=5000):
 +    &#​34;&#​34;&#​34;​
 +    Powers Vizy off. The `t` argument specifies how long 
 +    to wait before turning off (specified in milliseconds). ​ The 
 +    vizy-powerd service calls this upon shutdown.
 +    &#​34;&#​34;&#​34;​
 +    self.bus.write_i2c_block_data(self.addr,​ 38, [0x1f, int(t/​100)])</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.power_off_requested"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​power_off_requested</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns <​code>​True</​code>​ if Vizy's button is held down for more than 5 seconds
 +indicating that the user wishes to initiate safe shutdown and power
 +off.
 +Returns <​code>​False</​code>​ otherwise.</​p>​
 +<​p>​This is used by the vizy-powerd service.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def power_off_requested(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns `True` if Vizy&#​39;​s button is held down for more than 5 seconds
 +    indicating that the user wishes to initiate safe shutdown and power
 +    off.  Returns `False` otherwise.
 +
 +    This is used by the vizy-powerd service. ​
 +    &#​34;&#​34;&#​34;​
 +    button = self.bus.read_i2c_block_data(self.addr,​ 38, 1)
 +    if button[0]==0x0f:​
 +        return True
 +    else:
 +        return False</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.power_on_alarm_date"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​power_on_alarm_date</​span></​span>​(<​span>​self,​ datetime_=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​If you wish to power off your Vizy and have it "wake up" at a
 +specified time and date, call this method with the desired
 +datetime object and initiate a shutdown. (e.g. <​code>​sudo shutdown now</​code>​).</​p>​
 +<​p>​The code below tells Vizy to turn on on December 2, 2022, 11:​18am.</​p>​
 +<​pre><​code>​import vizypowerboard as vpb
 +from datetime import datetime
 +v = vpb.VizyPowerBoard()
 +d=datetime(year=2022,​ month=12, day=2, hour=11, minute=18, second=0)
 +v.power_on_alarm_date(d)
 +</​code></​pre>​
 +<h2 id="​args">​Args</​h2>​
 +<dl>
 +<​dt><​strong><​code>​datetime_</​code></​strong>​ :&​ensp;<​code>​datetime</​code>,​ optional, default=<​code>​None</​code></​dt>​
 +<​dd><​code>​datetime</​code>​ object that
 +specifies the date/time to "wake up" (turn on).</​dd>​
 +</dl>
 +<h2 id="​returns">​Returns</​h2>​
 +<​p>​Calling without a datetime object returns a <​code>​datetime</​code>​ object
 +reflecting the active alarm time.
 +If there is no active alarm,
 +<​code>​None</​code>​ is returned.</​p>​
 +<h2 id="​notes">​Notes</​h2>​
 +<ul>
 +<​li>​Once setting the alarm date, Vizy will retain it even if Vizy loses
 +power for extended periods of time.</​li>​
 +<​li>​If the alarm expires while Vizy is on, Vizy will emit a buzzer tone
 +and remain on.</​li>​
 +<​li>​If the alarm expires while Vizy is off (but plugged into and
 +receiving power), Vizy will turn on.</​li>​
 +<​li>​If the alarm expires while Vizy is unplugged from (or not receiving)
 +power, Vizy will turn on as soon as it receives power.</​li>​
 +</​ul></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def power_on_alarm_date(self,​ datetime_=None):​
 +    &#​34;&#​34;&#​34;​
 +    If you wish to power off your Vizy and have it &#​34;​wake up&#34; at a
 +    specified time and date, call this method with the desired
 +    datetime object and initiate a shutdown. (e.g. `sudo shutdown now`).
 +
 +
 +    The code below tells Vizy to turn on on December 2, 2022, 11:18am.
 +
 +        import vizypowerboard as vpb
 +        from datetime import datetime
 +        v = vpb.VizyPowerBoard()
 +        d=datetime(year=2022,​ month=12, day=2, hour=11, minute=18, second=0)
 +        v.power_on_alarm_date(d)
 +
 +    Args:
 +      datetime_ (datetime, optional, default=None):​ `datetime` object that
 +        specifies the date/time to &#​34;​wake up&#34; (turn on).
 +
 +    Returns:
 +      Calling without a datetime object returns a `datetime` object ​
 +      reflecting the active alarm time.  If there is no active alarm,
 +      `None` is returned.
 +       
 +    Notes:
 +      * Once setting the alarm date, Vizy will retain it even if Vizy loses
 +      power for extended periods of time.
 +      * If the alarm expires while Vizy is on, Vizy will emit a buzzer tone
 +      and remain on.
 +      * If the alarm expires while Vizy is off (but plugged into and
 +      receiving power), Vizy will turn on.
 +      * If the alarm expires while Vizy is unplugged from (or not receiving)
 +      power, Vizy will turn on as soon as it receives power.  ​
 +    &#​34;&#​34;&#​34;​
 +    if datetime_ is None:
 +        t = self.bus.read_i2c_block_data(self.addr,​ 41, 6)
 +        if t[5]==0:
 +            return None
 +        return datetime.datetime(year=self._bcd2decimal(t[5])+2016,​ month=self._bcd2decimal(t[4]),​ day=self._bcd2decimal(t[3]),​ hour=self._bcd2decimal(t[2]),​ minute=self._bcd2decimal(t[1]),​ second=self._bcd2decimal(t[0]))
 +    t = [self._decimal2bcd(datetime_.second),​ self._decimal2bcd(datetime_.minute),​ self._decimal2bcd(datetime_.hour),​ self._decimal2bcd(datetime_.day),​ self._decimal2bcd(datetime_.month),​ self._decimal2bcd(datetime_.year-2016)]
 +    self.bus.write_i2c_block_data(self.addr,​ 41, t)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.power_on_alarm_seconds"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​power_on_alarm_seconds</​span></​span>​(<​span>​self,​ seconds=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Allows you to specify a power on alarm in seconds in the future.
 +For example, if you wish for Vizy to turn back on in 5 minutes, you
 +would call <​code>​power_on_alarm_seconds(300)</​code>​ and then initiate a shutdown.
 +See <​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_alarm_date"​ href="#​vizypowerboard.VizyPowerBoard.power_on_alarm_date">​VizyPowerBoard.power_on_alarm_date()</​a></​code>​ for more information about the power on
 +alarm.</​p>​
 +<h2 id="​args">​Args</​h2>​
 +<dl>
 +<​dt><​strong><​code>​seconds</​code></​strong>​ :&​ensp;<​code>​integer</​code>,​ optional, default=<​code>​None</​code></​dt>​
 +<​dd>​Number of seconds in the
 +future you wish Vizy to turn on in.</​dd>​
 +</dl>
 +<h2 id="​returns">​Returns</​h2>​
 +<​p>​Calling this method without arguments returns the number of seconds
 +until the alarm expires.
 +If no alarm is pending, <​code>​None</​code>​ is returned.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def power_on_alarm_seconds(self,​ seconds=None):​
 +    &#​34;&#​34;&#​34;​
 +    Allows you to specify a power on alarm in seconds in the future. ​
 +    For example, if you wish for Vizy to turn back on in 5 minutes, you
 +    would call `power_on_alarm_seconds(300)` and then initiate a shutdown.
 +    See `VizyPowerBoard.power_on_alarm_date()` for more information about the power on 
 +    alarm.
 +
 +    Args:
 +      seconds (integer, optional, default=None):​ Number of seconds in the
 +        future you wish Vizy to turn on in.
 +
 +    Returns:
 +      Calling this method without arguments returns the number of seconds
 +      until the alarm expires. ​ If no alarm is pending, `None` is returned.
 +    &#​34;&#​34;&#​34;​
 +    if seconds is None:
 +        pod = self.power_on_alarm_date()
 +        if pod is None:
 +            return None
 +        diff = pod - self.rtc()
 +        return diff.days*86400+diff.seconds ​
 +    # Add seconds to current time and set power on alarm    ​
 +    self.power_on_alarm_date(self.rtc()+datetime.timedelta(seconds=seconds))</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.power_on_source"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​power_on_source</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns the source of what turned on Vizy for the current power cycle.
 +It is one of either:</​p>​
 +<ul>
 +<​li>​POWER_ON_SOURCE_ALARM,​ indicates that Vizy was powered on
 +by the power on alarm expiring.
 +See power_on_alarm_date() and
 +power_on_alarm_seconds().</​li>​
 +<​li>​POWER_ON_SOURCE_POWER_BUTTON,​ indicates that Vizy was powered
 +on by someone pressing the button.</​li>​
 +<​li>​POWER_ON_SOURCE_12V = indicates that Vizy was powered on
 +by power being applied to 12V power input.
 +This only applies if the
 +dip switch power mode allows powering on by plugging in power via the
 +12V power input.</​li>​
 +<​li>​POWER_ON_SOURCE_5V,​ indicates that Vizy was powered on by applying
 +power to the Raspberry Pi's USB-C power input.</​li>​
 +</​ul></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def power_on_source(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns the source of what turned on Vizy for the current power cycle.
 +    It is one of either:
 +
 +    * POWER_ON_SOURCE_ALARM,​ indicates that Vizy was powered on
 +    by the power on alarm expiring. ​ See power_on_alarm_date() and
 +    power_on_alarm_seconds().
 +    * POWER_ON_SOURCE_POWER_BUTTON,​ indicates that Vizy was powered ​
 +    on by someone pressing the button.
 +    * POWER_ON_SOURCE_12V = indicates that Vizy was powered on
 +    by power being applied to 12V power input. ​ This only applies if the
 +    dip switch power mode allows powering on by plugging in power via the
 +    12V power input.
 +    * POWER_ON_SOURCE_5V,​ indicates that Vizy was powered on by applying
 +    power to the Raspberry Pi&#​39;​s USB-C power input.
 +    &#​34;&#​34;&#​34;​
 +    source = self.bus.read_i2c_block_data(self.addr,​ 40, 1)[0]
 +    return source</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.resource_url"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​resource_url</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns the url of a JSON file that contains information about
 +resources, such as the location of the latest version of this code,
 +latest firmware, etc.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def resource_url(self):​
 +    &#​34;&#​34;&#​34;​
 +    Returns the url of a JSON file that contains information about 
 +    resources, such as the location of the latest version of this code, 
 +    latest firmware, etc.
 +    &#​34;&#​34;&#​34;​
 +    chars = self.bus.read_i2c_block_data(self.addr,​ 6, 32)
 +    s = &#​39;&#​39;​
 +    for c in chars:
 +        if c==0: # read up to the null character
 +            break
 +        s += chr(c)
 +    return s</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.rtc"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​rtc</​span></​span>​(<​span>​self,​ datetime_=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Set or get the real-time clock time/date.
 +The Vizy power board has a
 +battery-backed real-time clock that keeps track of time/date, power
 +alarms, etc. even while Vizy is receiving no power.
 +Passing in a
 +datetime object sets the time/date.
 +</p>
 +<​p>​Calling this method with no
 +arguments returns a datetime object representing the current
 +date/time. </p>
 +<​p>​For example, the code below sets the date to December 2, 2020, 11:​18am:</​p>​
 +<​pre><​code>​from datetime import datetime
 +import vizypowerboard as vpb
 +v = vpb.VizyPowerBoard()
 +t = datetime(year=2020,​ month=12, day=2, hour=11, minute=18, second=0)
 +v.rtc(t)
 +</​code></​pre></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def rtc(self, datetime_=None):​
 +    &#​34;&#​34;&#​34;​
 +    Set or get the real-time clock time/​date. ​ The Vizy power board has a 
 +    battery-backed real-time clock that keeps track of time/date, power 
 +    alarms, etc. even while Vizy is receiving no power. ​ Passing in a
 +    datetime object sets the time/​date.  ​
 +
 +    Calling this method with no
 +    arguments returns a datetime object representing the current ​
 +    date/​time. ​
 +
 +    For example, the code below sets the date to December 2, 2020, 11:18am:
 +
 +        from datetime import datetime
 +        import vizypowerboard as vpb
 +        v = vpb.VizyPowerBoard()
 +        t = datetime(year=2020,​ month=12, day=2, hour=11, minute=18, second=0)
 +        v.rtc(t)
 +    &#​34;&#​34;&#​34;​
 +    if datetime_ is None:
 +        # Initiate RTC retrieval.
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC])
 +        # Wait until it&#​39;​s ready.
 +        self._wait_until_not_busy()
 +
 +        t = self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 8)
 +        return datetime.datetime(year=self._bcd2decimal(t[7])+2016,​ month=self._bcd2decimal(t[6]),​ day=self._bcd2decimal(t[4]),​ hour=self._bcd2decimal(t[3]),​ minute=self._bcd2decimal(t[2]),​ second=self._bcd2decimal(t[1]))
 +
 +    t = [EXEC_RTC|EXEC_WRITE,​ 0, self._decimal2bcd(datetime_.second),​ self._decimal2bcd(datetime_.minute),​ self._decimal2bcd(datetime_.hour),​ self._decimal2bcd(datetime_.day),​ 0, self._decimal2bcd(datetime_.month),​ self._decimal2bcd(datetime_.year-2016)]
 +    self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ t)
 +    self._wait_until_not_busy()</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.rtc_adjust"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​rtc_adjust</​span></​span>​(<​span>​self,​ val=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Set or get the real-time clock adjustment.
 +Vizy's real-time clock
 +crystal has an accuracy of 20ppm, which means that it can lose or gain
 +up to 20 seconds for every 1 million elapsed seconds.
 +Normally, this
 +isn't an issue, but if Vizy spends a lengthy period of time (months)
 +without Internet access, it could lose or gain minutes, which
 +depending on the application could be significant. The adjustment
 +value can offset this inaccuracy.
 +The <​code>​val</​code>​ argument can range
 +between -128 and 127 and has a multiplier of 2.170 ppm.
 +</p>
 +<​p>​For example,
 +if the RTC is gaining 10 seconds every 1 million seconds, you would
 +call <​code>​rtc_adjust(-5)</​code>​.
 +If the RTC is losing 10 seconds every million
 +seconds you would call <​code>​rtc_adjust(5)</​code>​.</​p>​
 +<​p>​The adjustment value is retained by the real-time clock even when
 +Vizy's power is removed.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def rtc_adjust(self,​ val=None):
 +    &#​34;&#​34;&#​34;​
 +    Set or get the real-time clock adjustment. ​ Vizy&#​39;​s real-time clock 
 +    crystal has an accuracy of 20ppm, which means that it can lose or gain 
 +    up to 20 seconds for every 1 million elapsed seconds. ​ Normally, this
 +    isn&#​39;​t an issue, but if Vizy spends a lengthy period of time (months)
 +    without Internet access, it could lose or gain minutes, which 
 +    depending on the application could be significant. The adjustment
 +    value can offset this inaccuracy. ​ The `val` argument can range 
 +    between -128 and 127 and has a multiplier of 2.170 ppm.  ​
 +
 +    For example, ​
 +    if the RTC is gaining 10 seconds every 1 million seconds, you would 
 +    call `rtc_adjust(-5)`. ​ If the RTC is losing 10 seconds every million
 +    seconds you would call `rtc_adjust(5)`.
 +    ​
 +    The adjustment value is retained by the real-time clock even when 
 +    Vizy&#​39;​s power is removed. ​
 +    &#​34;&#​34;&#​34;​
 +    if val is None:
 +        self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC_CALIBRATE])
 +        # Wait until it&#​39;​s ready.
 +        self._wait_until_not_busy()
 +        return self.bus.read_i2c_block_data(self.addr,​ EXEC_OFFSET+1,​ 1)[0]
 +
 +    self.bus.write_i2c_block_data(self.addr,​ EXEC_OFFSET,​ [EXEC_RTC_CALIBRATE|EXEC_WRITE,​ self._int8(val)])
 +    self._wait_until_not_busy()</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.rtc_set_system_datetime"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​rtc_set_system_datetime</​span></​span>​(<​span>​self,​ datetime_=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​A convenience method that sets the system time/date based on the
 +real-time time/date.
 +This is called by vizy-powerd upon power-up.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def rtc_set_system_datetime(self,​ datetime_=None):​
 +    &#​34;&#​34;&#​34;​
 +    A convenience method that sets the system time/date based on the 
 +    real-time time/​date. ​ This is called by vizy-powerd upon power-up. ​
 +    &#​34;&#​34;&#​34;​
 +    if os.geteuid()!=0:​
 +        raise PermissionError(&#​34;​You need root permission to set the time/​date.&#​34;​)
 +    if datetime_ is None:
 +        datetime_ = self.rtc()
 +    s = datetime_.isoformat()
 +    os.system(f&#​34;​sudo date -s {s}&#​34;​)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.uuid"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​uuid</​span></​span>​(<​span>​self)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​Returns a 16-byte unique ID that can be used an a unique
 +ID for your Vizy camera.
 +This unique ID is stored on the Vizy Power
 +Board and remains constant regardless of firmware upgrades, etc.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def uuid(self):
 +    &#​34;&#​34;&#​34;​
 +    Returns a 16-byte unique ID that can be used an a unique
 +    ID for your Vizy camera. ​ This unique ID is stored on the Vizy Power
 +    Board and remains constant regardless of firmware upgrades, etc.
 +    &#​34;&#​34;&#​34;​
 +    return self.bus.read_i2c_block_data(self.addr,​ 22, 16)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.vcc12"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​vcc12</​span></​span>​(<​span>​self,​ state=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​If <​code>​state</​code>​ is <​code>​True</​code>,​ the 12V output on Vizy's I/O connector (pin 2) will be enabled and output 12V.
 +If <​code>​state</​code>​ is <​code>​False</​code>,​ the 12V output
 +will be disabled.
 +Calling without arguments returns its current state.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def vcc12(self, state=None):​
 +    &#​34;&#​34;&#​34;​
 +    If `state` is `True`, the 12V output on Vizy&#​39;​s I/O connector (pin 2) will be enabled and output 12V.  If `state` is `False`, the 12V output
 +    will be disabled. ​ Calling without arguments returns its current state.
 +    &#​34;&#​34;&#​34; ​
 +    config = self.bus.read_i2c_block_data(self.addr,​ 48, 1)
 +    if state is None:
 +        return True if config&​amp;​0x01 else False
 +    if state:
 +        config[0] |= 0x01
 +    else:
 +        config[0] &amp;= ~0x01
 +
 +    self.bus.write_i2c_block_data(self.addr,​ 48, config)</​code></​pre>​
 +</​details>​
 +</dd>
 +<dt id="​vizypowerboard.VizyPowerBoard.vcc5"><​code class="​name flex">​
 +<​span>​def <span class="​ident">​vcc5</​span></​span>​(<​span>​self,​ state=None)</​span>​
 +</​code></​dt>​
 +<dd>
 +<div class="​desc"><​p>​If <​code>​state</​code>​ is <​code>​True</​code>,​ the 5V output on Vizy's I/O connector (pin 3) will
 +be enabled and output 5V.
 +If <​code>​state</​code>​ is <​code>​False</​code>,​ the 5V output will be
 +disabled.
 +Calling without arguments returns its current state.</​p></​div>​
 +<details class="​source">​
 +<​summary>​
 +<​span>​Expand source code</​span>​
 +</​summary>​
 +<​pre><​code class="​python">​def vcc5(self, state=None):​
 +    &#​34;&#​34;&#​34;​
 +    If `state` is `True`, the 5V output on Vizy&#​39;​s I/O connector (pin 3) will
 +    be enabled and output 5V.  If `state` is `False`, the 5V output will be
 +    disabled. ​ Calling without arguments returns its current state.
 +    &#​34;&#​34;&#​34; ​
 +    if state is None:
 +        return True if config&​amp;​0x02 else False
 +    if state:
 +        config[0] |= 0x02
 +    else:
 +        config[0] &amp;= ~0x02
 +
 +    self.bus.write_i2c_block_data(self.addr,​ 48, config)</​code></​pre>​
 +</​details>​
 +</dd>
 +</dl>
 +</dd>
 +</dl>
 +</​section>​
 +</​article>​
 +<nav id="​sidebar">​
 +<​h1>​Index</​h1>​
 +<div class="​toc">​
 +<​ul></​ul>​
 +</​div>​
 +<ul id="​index">​
 +<​li><​h3><​a href="#​header-variables">​Global variables</​a></​h3>​
 +<ul class="">​
 +<​li><​code><​a title="​vizypowerboard.CHANNEL_5V"​ href="#​vizypowerboard.CHANNEL_5V">​CHANNEL_5V</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.CHANNEL_VIN"​ href="#​vizypowerboard.CHANNEL_VIN">​CHANNEL_VIN</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_EXT_BUTTON"​ href="#​vizypowerboard.DIPSWITCH_EXT_BUTTON">​DIPSWITCH_EXT_BUTTON</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_MUTE_BUZZER"​ href="#​vizypowerboard.DIPSWITCH_MUTE_BUZZER">​DIPSWITCH_MUTE_BUZZER</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_NO_BG_LED"​ href="#​vizypowerboard.DIPSWITCH_NO_BG_LED">​DIPSWITCH_NO_BG_LED</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_POWER_DEFAULT_OFF"​ href="#​vizypowerboard.DIPSWITCH_POWER_DEFAULT_OFF">​DIPSWITCH_POWER_DEFAULT_OFF</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_POWER_DEFAULT_ON"​ href="#​vizypowerboard.DIPSWITCH_POWER_DEFAULT_ON">​DIPSWITCH_POWER_DEFAULT_ON</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_POWER_PLUG"​ href="#​vizypowerboard.DIPSWITCH_POWER_PLUG">​DIPSWITCH_POWER_PLUG</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.DIPSWITCH_POWER_SWITCH"​ href="#​vizypowerboard.DIPSWITCH_POWER_SWITCH">​DIPSWITCH_POWER_SWITCH</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.IO_MODE_HIGH_CURRENT"​ href="#​vizypowerboard.IO_MODE_HIGH_CURRENT">​IO_MODE_HIGH_CURRENT</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.IO_MODE_INPUT"​ href="#​vizypowerboard.IO_MODE_INPUT">​IO_MODE_INPUT</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.IO_MODE_OUTPUT"​ href="#​vizypowerboard.IO_MODE_OUTPUT">​IO_MODE_OUTPUT</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.POWER_ON_SOURCE_12V"​ href="#​vizypowerboard.POWER_ON_SOURCE_12V">​POWER_ON_SOURCE_12V</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.POWER_ON_SOURCE_5V"​ href="#​vizypowerboard.POWER_ON_SOURCE_5V">​POWER_ON_SOURCE_5V</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.POWER_ON_SOURCE_ALARM"​ href="#​vizypowerboard.POWER_ON_SOURCE_ALARM">​POWER_ON_SOURCE_ALARM</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.POWER_ON_SOURCE_POWER_BUTTON"​ href="#​vizypowerboard.POWER_ON_SOURCE_POWER_BUTTON">​POWER_ON_SOURCE_POWER_BUTTON</​a></​code></​li>​
 +</ul>
 +</li>
 +<​li><​h3><​a href="#​header-classes">​Classes</​a></​h3>​
 +<ul>
 +<li>
 +<​h4><​code><​a title="​vizypowerboard.VizyPowerBoard"​ href="#​vizypowerboard.VizyPowerBoard">​VizyPowerBoard</​a></​code></​h4>​
 +<ul class="">​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.button"​ href="#​vizypowerboard.VizyPowerBoard.button">​button</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.button_pressed"​ href="#​vizypowerboard.VizyPowerBoard.button_pressed">​button_pressed</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.buzzer"​ href="#​vizypowerboard.VizyPowerBoard.buzzer">​buzzer</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.dip_switches"​ href="#​vizypowerboard.VizyPowerBoard.dip_switches">​dip_switches</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.fan"​ href="#​vizypowerboard.VizyPowerBoard.fan">​fan</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.fw_version"​ href="#​vizypowerboard.VizyPowerBoard.fw_version">​fw_version</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.hw_version"​ href="#​vizypowerboard.VizyPowerBoard.hw_version">​hw_version</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.io_bits"​ href="#​vizypowerboard.VizyPowerBoard.io_bits">​io_bits</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.io_set_mode"​ href="#​vizypowerboard.VizyPowerBoard.io_set_mode">​io_set_mode</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.ir_filter"​ href="#​vizypowerboard.VizyPowerBoard.ir_filter">​ir_filter</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.led"​ href="#​vizypowerboard.VizyPowerBoard.led">​led</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.led_background"​ href="#​vizypowerboard.VizyPowerBoard.led_background">​led_background</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.led_unicorn"​ href="#​vizypowerboard.VizyPowerBoard.led_unicorn">​led_unicorn</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.measure"​ href="#​vizypowerboard.VizyPowerBoard.measure">​measure</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.power_off"​ href="#​vizypowerboard.VizyPowerBoard.power_off">​power_off</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.power_off_requested"​ href="#​vizypowerboard.VizyPowerBoard.power_off_requested">​power_off_requested</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_alarm_date"​ href="#​vizypowerboard.VizyPowerBoard.power_on_alarm_date">​power_on_alarm_date</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_alarm_seconds"​ href="#​vizypowerboard.VizyPowerBoard.power_on_alarm_seconds">​power_on_alarm_seconds</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.power_on_source"​ href="#​vizypowerboard.VizyPowerBoard.power_on_source">​power_on_source</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.resource_url"​ href="#​vizypowerboard.VizyPowerBoard.resource_url">​resource_url</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.rtc"​ href="#​vizypowerboard.VizyPowerBoard.rtc">​rtc</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.rtc_adjust"​ href="#​vizypowerboard.VizyPowerBoard.rtc_adjust">​rtc_adjust</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.rtc_set_system_datetime"​ href="#​vizypowerboard.VizyPowerBoard.rtc_set_system_datetime">​rtc_set_system_datetime</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.uuid"​ href="#​vizypowerboard.VizyPowerBoard.uuid">​uuid</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.vcc12"​ href="#​vizypowerboard.VizyPowerBoard.vcc12">​vcc12</​a></​code></​li>​
 +<​li><​code><​a title="​vizypowerboard.VizyPowerBoard.vcc5"​ href="#​vizypowerboard.VizyPowerBoard.vcc5">​vcc5</​a></​code></​li>​
 +</ul>
 +</li>
 +</ul>
 +</li>
 +</ul>
 +</​nav>​
 +</​main>​
 +<footer id="​footer">​
 +<​p>​Generated by <a href="​https://​pdoc3.github.io/​pdoc"><​cite>​pdoc</​cite>​ 0.9.2</​a>​.</​p>​
 +</​footer>​
 +</​body>​
 +</​html>​
 +</​HTML>​
  
wiki/test.1612479211.txt.gz · Last modified: 2021/02/04 16:53 by vizycam