<< 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/13 13:14]
vizycam
wiki:test [2021/10/16 18:30] (current)
vizycam
Line 1: Line 1:
-Set or get the (virtual) DIP switch state. ​ The DIP switches are a set  +[[wiki:running_utilities|utilities]]
-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. ​ hello [here](https://​vizycamcam.com). +
-The switches are a set of values that can be ORed together:+
  
-* DIPSWITCH_EXT_BUTTON,​ used to set external/​remote power button,  +[[wiki:​pinoouts|pinouts]] 
-e.g. with outdoor enclosure. ​ Default disabled. + 
-* DIPSWITCH_MUTE_BUZZER,​ used to mute the buzzer. ​ Default disabled. +[[wiki:​ssh|ssh]] 
-* DIPSWITCH_NO_BG_LED,​ used to disable the background LED, which is  + 
-normally set to yellow upon power up.  Default disabled. +[[wiki:​birdfeeder_app|birdfeeder]] 
-* DIPSWITCH_POWER_DEFAULT_OFF,​ if this power mode is set and you + 
 +[[wiki:​stage|stage]] 
 + 
 +hello 
 +<​HTML>​ 
 +<​!doctype html> 
 +<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 
 + 
 +FILE_VERSION = 1 
 +COMPAT_HW_VERSION = [3, 0] 
 + 
 +ERROR_GENERIC = 255 
 +ERROR_BUSY = 254 
 +ERROR_READ_ONLY = 253 
 +ERROR_NOT_SUPPORTED = 252 
 + 
 +EXEC_OFFSET = 0xc0 
 +EXEC_RW = 0x80 
 +EXEC_WRITE = 0x80 
 +EXEC_NVCONFIG = 1 
 +EXEC_AD = 2 
 +EXEC_DA = 3 
 +EXEC_RTC = 16 
 +EXEC_RTC_CALIBRATE = 17 
 + 
 +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 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 +until you press the button to power Vizy on. 
-interrupted while Vizy is on, *Vizy will turn off* If power is +And if power is 
-interrupted while Vizy is off, Vizy will remain off.  This is the +interrupted while Vizy is on, <em>Vizy will turn off</em>. 
-default power mode. +If power is 
-DIPSWITCH_POWER_DEFAULT_ON,​ if this power mode is set and you +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 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 +without pressing the button. 
-is on, Vizy will reset, but remain on.  If power is interrupted while +And if power is interrupted while Vizy 
-Vizy is off, *Vizy will turn on* Default disabled. +is on, Vizy will reset, but remain on. 
-DIPSWITCH_POWER_SWITCH,​ if this power mode is set and you plug in+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 power via the 12V power input, Vizy will remain off (as in
-DIPSWITCH_POWER_DEFAULT_OFF mode), unless power was  +DIPSWITCH_POWER_DEFAULT_OFF mode), unless power was 
-removed while Vizy was on.  In this case Vizy will turn on when you +removed while Vizy was on. 
-(re)apply power. ​ If power is interrupted while Vizy is off, Vizy  +In this case Vizy will turn on when you 
-will remain off.  This behavior is similar to the behavior of a real+(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 power switch in that it retains the power "​state"​ (on or off) and acts
-accordingly. ​ Default disabled. +accordingly. 
-DIPSWITCH_POWER_PLUG,​ if this power mode is set Vizy will remain +Default disabled.</li> 
-powered on as long as it receives power through the 12V power plug, +<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 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.+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
  
-For example+    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
  
-    ​dip_switches(DIPSWITCH_EXT_BUTTON ​DIPSWITCH_POWER_SWITCHset external power button ​and power switch mode+    ​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.1613243672.txt.gz · Last modified: 2021/02/13 13:14 by vizycam