Home Projects AVR Programming Guides Electrical Fundamentals Insteon Saving Electricity

Recent Updates

EasyGrow Greenhouse
Follow our adventure of assembling an 8x12 EasyGrow Greenhouse.
AVR Programming Guides
We have some nice AVR code samples to share. Lots of AVR goodies like timers, interrupts, and pin I/O for the beginner.
Watts Tables
Use these tables to help you determine how much various items in your house cost you by the day, month, and year.
Saving Electricity
A list of a variety of ways to help you save money by learning how much it costs to run things.
What is a Kilowatt Hour
Saving money on your electric bill starts with understanding what a kilowatt hour is.
How To Measure Watts
Learn a variety of ways to measure watts in your house.
Read Your Power Meter
By reading your power meter you can get a good idea of how much various electrical devices cost you.
Electronics Fundamentals
Learn some basic electronics fundamentals and see if you enjoy the field of electronics.














Insteon Device Manager Commands

Quick Reference Guide for Smarthome Device Manager for INSTEON
(matches alpha-release - 1.10 - 2005-08-25)

INTRODUCTION

The Smarthome Device Manager (SDM) is a communication and translation gateway to using the PowerLinc Controller (#2414). Developers use simple text commands (Home Networking Language) through ActiveX or HTTP calls to communicate over INSTEON or X10 without the hassle of dealing with USB packets or RS232 resource issues.

These commands will also be extended to PowerLinc/IP and other internet transports.

Using the Smarthome Device Manager, developers can focus on their application, whether in Macromedia Flash, Java, .NET or even Word, Excel or a PowerPoint.

Commands such as "SetOnLevelText=01.02.03,ON" perform the action and return a response, providing developers with simple and reliable control. Additional commands such as "speak," inet," and "mailto" further provide the developer with powerful capabilities.

SDM Platforms: Windows XP, 2000, NT (serial only), Me, 98, and 95 (serial only).
SDM Programming Platforms: any language that provides either ActiveX, HTTP, or shell calls including (Java, VB, .NET, C#, C++, Delphi, Flash, Perl, ASP, PHP, VB/W/JScript, Tcl, Python and more).
SDM Resources: HTTP Server uses port 9020 and offers a server-pull method to facilitate firewalls. Connects via COM1~COM255 or USB.
PowerLinc Controller Firmware Requirement: 2.8 or better
HOME CONTROL COMMANDS
setOnLevelText=<INSTEONid>,<onLevelCmdOrValue>[,<hops>] - set on-level status (ON/OFF/dec%/dec/0xHex) of an ID, optionally specifying the number of hops. (defaults to 3 hops).
Examples: setOnLevelText=04.05.06,ON or setOnLevelText=04.05.06,49% or setOnLevelText=04.05.06,127 or setOnLevelRaw=04.05.06,0x7F
getOnLevelText=<INSTEONid>[,<hops>] - get on-level status from an ID as a text representation, optionally specifying the number of hops. (defaults to 3 hops). Returns ON,OFF,OUT,or percentage of on (1-99%).
Examples: getOnLevelText=04.05.06. Returns getOnLevelText=04.05.06,ON or getOnLevelText=04.05.06,49%
sendX10=<addressOrCommand>[,<addressOrCommand>] - sends x10 addresses and commands. Example: sendX10=A1,AON or sendX10=A1,A2 or sendX10=AON
sendGroupBroadcast=<groupID>,<cmd1>[,<cmd2>][,<hops>] - sends a broadcast from the PowerLinc Controller with the included group ID, and command1 (ON/OFF/hex), and optional command2 (defaults to 0) and hops (defaults to 3).
NOTIFICATION RESPONSES (uninitiated messages)
eventRaw=<eventID> - notification of an event in hex.
Example: eventRaw=0A
receiveX10=<AddressOrCommand> - notification of an X10 address or command received, in text.
Example: receiveX10=A1 or receiveX10=A On
receiveX10raw=<x10type> <AddressOrCommandByte> - notification of an X10 address or command received, in hex form. x10type is zero (0) for an address, or one (1) for a command.
Example: receiveX10raw=00 66
receiveINSTEONraw=<eventID> <messageBytes...> - notification of an INSTEON message received. The number of bytes varies depending upon the eventID and whether the message is standard or extended.
Example: receiveINSTEONraw=02 04 05 06 00 00 11 8F 01 00
enrolled=<INSTEONid>,<deviceInfoBytes>,<deviceName> - notification that an INSTEON device was enrolled into the PLC.
NOTE: linked= will replace enrolled= ... linked=<INSTEONid>, <groupID>, <deviceInfoBytes>, <responderOrController>[, <deviceSKU>, <deviceName>]
usbarrival=<VenderID>,<PorductID>,<Version>,<Company>,<ProductName> - notification that the PLC was connected. (Specific to PLC from Smarthome).
Example: usbarrival=4287,4,1024,SmartHome,SmartHome PowerLinc USB E,
usbunplugged=4287,4,1024,,, - notification that the PLC was disconnected (once connected). (Specific to PLC from Smarthome).
Example: usbunplugged=4287,4,1024,,,
ProgressStart - indicates that a download in about to start.
ProgressMax=<maxBytes> - indicates that the download consists of <maxBytes>. Used to set the max of a progressbar.
Progress=<percent%>,<byteCount>,<maxBytes> - indicates that <byteCount> bytes has been downloaded of the whole <maxBytes>, which represents <percent%> portion of the whole download.
ProgressError=<percent%>,<byteCount>,<maxBytes> - indicates that an error occured while downloading <byteCount> bytes of the whole <maxBytes>, which represents <percent%> portion of the whole download. Repeating will occur until 'setPageErrors' number of consecutive erros have occurred. IF this occurs, try setting the setPageSize to a smaller value, such as 64 or 16 (defaults to 256).
ProgressEnd - indicates that a download ended.
DIRECT COMMANDS (raw, low-level communication)
sendINSTEONraw=<9 or 23 hexbytes> - send the 9 (standard) or 23 (extended) INSTEONbytes from the PLC.
Example: sendINSTEONraw=01 02 03 04 05 06 0F 11 FF (send from unit 01.02.03 (which is overwritten with the actual PLC ID) to unit 04.05.06, with 3 hops, an ON at full brightness).
sendrecINSTEONraw/SRIR=<9 or 23 hexbytes> - send the 9 (standard) or 23 (extended) INSTEONbytes from the PLC *and wait for the response (ACK/NAK)*.
Examples: sendrecINSTEONraw=01 02 03 04 05 06 0F 11 FF or SRIR=01 02 03 07 08 09 0F 13 00. Returns SRIR=04,07 08 09 01 02 03 2F 13 00 (the returned ACK packet response).
getOnLevelRaw=<INSTEONid>[,<hops>] - get on-level status from an ID as a hexbyte representation, optionally specifying the number of hops. (defaults to 3 hops). Returns 00-FF.
Examples: getOnLevelRaw=04.05.06. Returns getOnLevelRaw=04.05.06,7F
setOnLevelRaw=<INSTEONid>,<onLevelCmdOrValue>[,<hops>] - set on-level status from an ID as a hexbyte representation, optionally specifying the number of hops. (defaults to 3 hops). Set/Returns 00-FF.
Examples: setOnLevelRaw=04.05.06,7F. Returns setOnLevelRaw=04.05.06,7F
sendX10raw=<x10type>,<AddressOrCommandByte> - send an X10 address or command byte. x10type is zero (0) for an address, or one (1) for a command.
Example: sendX10raw=00, 66 (sends A1 address).
sendPLC=<data...> - send direct raw hex bytes to the PLC, as if through a direct connection (such as serial).
Example: sendPLC=02 40 01 65 00 01 FF 33 66
sendEventRaw=<eventID> - sends an event (00-FF) for the PLC to execute (see INSTEON EVENTS).
MEMORY COMMANDS
setImage=<address>,<hexdata...> - downloads data to the address (map-friendly).
Examples: setImage=0x0040,02 FF or setImage=NTL_TIMERS,02 FF
getImage=<address>,<length> - uploads the image to the PC, returns hex bytes. (map-friendly).
Examples: getImage=0x0040,2 returns getImage=0x0040,00 00
setBit=<address>,<bit>[,<setTo0or1>] - sets the bit (0-7) at the address (map-friendly) with an optional value of 1(set) or 0(clear) (defaults to 1). Example setBit=0x0040,4 or setBit=0x0040,4,0
clearBit=<address>,<bit> - clears the bit (0-7) at the address (map-friendly). Example clearBit=0x0040,4
downloadCoreApp[=clear] - downloads the included core application to the PLC so that it can communicate to the Smarthome Device Manager. The optional =clear parameter also instructs the core application to clear the link table database after downloading. Automatically resets the PLC after download.
Example: downloadCoreApp
downloadSALadFile=<filename/url> - downloads the SALad program filename provided (as a binary/compiled file, not hex). Automatically resets the PLC after download. Using 'INSTEON.NET:' as the url prefix is an abbreviation for a common download area.
Example: downloadSALadFile=coreUser.slb or downloadSALadFile=INSTEON.NET:hdk.slb or downloadSALadFile=http://www.INSTEON.net/SALad/hdk.slb
downloadBinaryFile=<address>,<filename> - downloads the binary file to the address provided. This command does *not* automatically reset the PLC after download.
Example: downloadBinaryFile=<0x2000>,myTable.bin
verifyCoreApp - returns true or false with information whether the currently download application matches the expected core application.
Example: verifyCoreApp
PLC CONTROL COMMANDS
port=<PLCport> - sets the sticky, global PLC port (COM#,USB4,SIM28,?). Sending a question-mark '?' causes the port to be found. Use getport= to read the found port. The port is saved in the registry and reused upon restart of the Smarthome Device Manager.
Examples: port=COM1 or port=USB4 or port=? or port=SIM28
getport - returns the current sticky, global PLC port.
Example: getport returns getport=USB4
getFirmware - returns the PLC firmware revision.
Example: getFirmware returns getFirmware=2.9
sendHardReset - resets the SALad application, reinitializes (executes the INIT event).
getClock - gets the current PLC clock (which is reset from the RTClock on initialization/plugin in the Core Application).
Example: getClock returns getclock=true,8/12/2005 5:03:39 PM
getRTClock - gets the current real-time clock.
Example: getRTClock returns getclock=true,8/12/2005 5:03:39 PM
setClock - sets BOTH the real-time clock and the running clock.
Example: setClock=8/12/2004 5:03:39 PM
setRunningClock - sets only the running clock, not the real-time clock. This means that when the PLC is plugged in next or reset, the core app will copy the real-time clock over the running clock.
Example: setRunningClock=8/12/2004 5:03:39 PM
setRTClock - sets only the real-time clock, not the running clock. This means that when the PLC is plugged in next or reset, the core app will copy the real-time clock over the running clock.
Example: setRTClock=8/12/2004 5:03:39 PM
connect - connect to the PLC after a disconnection (connecting is done automatically at startup).
Example: connect
disconnect - disconnect the PLC's port connection.
Example: disconnect
isResponding - asks the Device Manager if the port is responding (sends 0x02 0x48) and responds true or false. This also reads the map for proper name-based downloading. This is the ultimate heartbeat method to determine if the PLC is connected and talking.
Example: isResponding - returns isResponding=true
getplcstatus - returns set of statuses for PowerLinc Controller (port, connection, etc).
Example: getplcstatus
help - provides a quick visual list of commands.
Example: help
getMyID - returns the PLC's ID.
Example: getMyID returns getMyID=01.02.03
DEVICE MANAGER COMMANDS
setTextMode=<textmode> - sets the global communication format that the Device Manager uses to receive and respond. Currently 'text' (default) and 'flash' are supported. 'flash' mode adds ampersands (&) around each response in order for the loadVariables() function to receive values from Macromedia Flash or SwishMax type clients.
nop - does nothing, but provides allows an HTTP connection to return collected data to the client.
echo - echoes the text simply to see if the ActiveX or HTTP communication is working.
_localbytes=<true/false> - turns on/off local (window) Device Manager byte-level debugging.
remotebytes=<true/false> - turns on/off local (window) Device Manager byte-level debugging.
setBlocked=<true/false> - (default false) turns on/off global blocking of commands. When blocked, the action is executed before receiving a response and returning to the client. When not blocked, the client is returned a true response that the command was accepted for execution by the Device Manager. When the actual response is received by the Device Manager, it is sent to the client via ActiveX, or queued for return via HTTP. (use NOP to get results when no execution action is necessary).
setPageSizeThrottle=<true/false> - (default true) allows global throttling of the downloads in case of errors. The download page size shrinks when multiple consecutive errors are received. The page size grows when multiple consecutive successes are received.
setPageErrors=<maxErrorCount> - (default 7) sets the global maximum number of consecutive retries before a download actually fails.
setPageSize=<pageSize> - (default 32) sets the global packet size for downloading. When throttling is true, this value automatically shrinks and grows.
if=<command>,<matchResult>,<trueText>[,<falseText>] - executes the command, matches against the matchResult. If true, returns the trueText. If false (and the falseText is present), returns the falseText.
Example: if=getFirmware,2.9,good,bad
ifexec=<command>,<matchResult>,<trueCommand>[,<falseCommand>] - executes the command, matches against the matchResult. If true, executes the trueCommand. If false (and the falseCommand is present), executes the falseCommand.
Example: ifexec=getFirmware,2.9,"setOnLevelText=00.02.BA,ON",nop
setAuthUsername=<username> - allows user to set an authorization username for the http/web connection to require. Shipped default is empty - no authorization required.
Example: setAuthUsername=me
setAuthPassword=<password> - allows user to set an authorization password for the http/web connection to require. Shipped default is empty - no authorization required. To clear, send setAuthPassword with no password.
Example: setAuthPassword=12345
GETTING STARTED COMMANDS
port=<PLCport> - sets the sticky, global PLC port (COM#,USB4,SIM28,?). Sending a question-mark '?' causes the port to be found. Use getport= to read the found port. The port is saved in the registry and reused upon restart of the Smarthome Device Manager.
Examples: port=COM1 or port=USB4 or port=? or port=SIM28
isResponding - asks the Device Manager if the port is responding (sends 0x02 0x48) and responds true or false. This also reads the map for proper name-based downloading. This is the ultimate heartbeat method to determine if the PLC is connected and talking.
Example: isResponding - returns isResponding=true
addid=<remoteINSTEONid> - add a device's ID to the PLC's database.
Example: addid=04.05.06
(only if necessary, such as the LED blinking or the PLC not communicating)
downloadCoreApp[=clear] - downloads the included core application to the PLC so that it can communicate to the Smarthome Device Manager. The optional =clear parameter also instructs the core application to clear the link table database after downloading.
Example: downloadCoreApp
setOnLevelText=<INSTEONid>, <onLevelCmdOrValue>[,<hops>] - set on-level status (ON/OFF/dec%/dec/0xHex) of an ID, optionally specifying the number of hops. (defaults to 3 hops).
Examples: setOnLevelText=04.05.06,ON or setOnLevelText=04.05.06,49% or setOnLevelText=04.05.06,127 or setOnLevelRaw=04.05.06,0x7F
QUICK TEST (HTTP)
1. Run Smarthome Device Manager (SDM2Server.exe) - a tray icon will appear.
2. Run a browser (such as I.E. or FireFox).
3. Type the following into as the URL: http://localhost:9020/abc.txt?isResponding
If running, you should see a textual response (isResponding=true) with other cached responses.
PLC EVENTS (hex)
00-Initialization. Occurs at powerup or when commanded to reset.
01-My Message: First (unique) message received for this unit (a new message has my ID as its destination).
02-A Message: First (unique) message received in a hop sequence.
03-Packet: Non-unique message received.
04-Acknowledge: An acknowledged message received for this unit (an ack message has my ID as its destination).
05-Non-Acknowledge: A sent message was sent and retried four times, but failed to receive an acknowledge.
06-Bad-ID: A message was received from a device that is not in my link table and not destined for me. Messages are masked with FFs for security.
07-Link Message: While being in link mode, a link message (Join Group or Button Pushed) was received.
08-X10 Message: An X10 message was received.
09-X10 Extended Message: An X10 extended message was received.
0A-Button Tap: My button was tapped. (Use TAP_CNT to determine how many times).
0B-Button Hold: My button was held (beyond 500ms).
0C-Button Released: My button was released.
0D-Tick: The tick counter has expired.
0E-Alarm: The alarm register (mins from midnight) matches the clock mins from midnight.
0F-Midnight: The clock rolled over to midnight.
10-2AM: The clock matches 2am (useful for Daylight Savings Time shift events).
11-Serial Byte: A serial byte was received that does not follow a known command.
12-Serial Command: A serial command was received that is unknown. (Useful to send for SALad processing).
13-Daughter: A daughtercard interrupt was received (HDK and other units).
14-3F-Reserved for future use.
40-FF-User events: You can send your own events and code for them using the sendEventRaw= command.
DATABASE MANAGEMENT COMMANDS
addID=<remoteINSTEONid> - add a device's ID to the PLC's database.
Example: addID=04.05.06
removeID=<remoteINSTEONid> - deletes a device's ID from the PLC's database.
Example: removeID=04.05.06
getRemoteRecord=<INSTEONid>, <record number>[,<end-range record number>] - gets and block-returns remote database records.
Examples: getRemoteRecord=04.05.06,2 or getRemoteRecord=04.05.06,2,3. Returns getremoterecord=
=====RECORDS BEGIN=====
remoterec(04.05.06):#2:A2 01 00 D0 80 FE 1F 00
remoterec(04.05.06):#3:A2 02 00 6B C2 FE 1F 00
=====RECORDS END=====

getLinks - returns a block-list of records in the PowerLinc Controller.
Example: getLinks

getRemoteGroupRecord=[<recno>:] <remoteINSTEONid>, <groupID> [,<sourceINSTEONid>] [,<hops>] - scans the remoteINSTEONid's (non-PLC) database for the sourceID (defaults to PLC's ID) and groupID, or uses the recno provided. Returns full record information as getRemoteGroupRecord=<remoteINSTEONid>, <sourceINSTEONid>, <groupID>, <onLevelText>, <rampRate>
Example: getRemoteGroupRecord=04.05.06, 1 returns getRemoteGroupRecord=04.05.06, 01.02.03,1,50%,31
setRemoteGroupRecord=[<recno>:] <remoteINSTEONid>, <groupID>,<sourceINSTEONid>,<hops>, <newPresetDim>, <newRampRate> - scans the remoteINSTEONid's (non-PLC) database for the sourceID (defaults to PLC's ID), and groupID, or uses the recno provided. Sets full record information.
Example: setRemoteGroupRecord=1:04.05.06, 1, 01.02.03,3,50%,31 or setRemoteGroupRecord=04.05.06, 1, 01.02.03,3,50%,31
setPresetDim=<remoteINSTEONid>, <groupID>, <newPresetDim> [,<sourceINSTEONid>] [,<hops>] - sets the preset dim value for the PLC or sourceINSTEONid (defaults to PLC's id) in the remoteINSTEONid's database.
Example: setPresetDim=04.05.06, 1, 25%
setRampRate=<remoteINSTEONid>, <groupID>, <newRampRate> [,<sourceINSTEONid>] [,<hops>] - sets the ramprate dim value for the PLC or sourceINSTEONid (defaults to PLC's id) in the remoteINSTEONid's database. (RAMPRATE VALUES ARE 0x00-slow to 0x1F-fast).
Example: setPresetDim=04.05.06, 1, 0x1F
getPresetDim=<remoteINSTEONid>, <groupID>, [,<sourceINSTEONid>] [,<hops>] - gets the preset dim value for the PLC or sourceINSTEONid (defaults to PLC's id) in the remoteINSTEONid's database.
Example: getPresetDim=04.05.06, 1
getRampRate=<remoteINSTEONid>, <groupID>, [,<sourceINSTEONid>] [,<hops>] - gets the ramprate dim value for the PLC or sourceINSTEONid (defaults to PLC's id) in the remoteINSTEONid's database. (RAMPRATE VALUES ARE 0x00-slow to 0x1F-fast).
Example: setPresetDim=04.05.06, 1

setupLink=<remoteINSTEONid>, <groupID>, [,<sourceINSTEONid>] [,<hops>] [,<presetDim>] [,<rampRate>] [,<sourceIs=C/R>] [,<flagByte>] [,<dataByte] - ensures (updates or adds) links between two devices to allow a complete linkset. I.E. if the remoteINSTEONid is a LampLinc and the sourceINSTEONid is a ControlLinc, then after using setupLink, the ControlLinc would control the LampLinc on the button determined by the groupID with the specified presetDim and rampRate. This command updates any previous links or creates them as necessary. sourceIs is either a 'C' for Controller or 'R' for Responder (note: a ControlLinc is a Controller for a LampLinc). flagByte is currently ignored and dataByte is not used in the devices. Note: hops are only used to create the setupLink. The hops are not a setting within the link.
Defaults are: sourceINSTEONid defaults to the PLC. hops defaults to 3. presetDim defaults to ON (0xFE). rampRate defaults to .1sec (0x1F). sourceIs defaults to C. flagByte defaults to 0xA2. dataByte defaults to 0x00. Note: setupLink automatically adds the remoteINSTEONid and the sourceINSTEONid (if not default) to the PLC's database for proper communication.

Example: setupLink=04.05.06,1 - sets up a link between the PLC (by default) and 04.05.06, group 1, with on level of 0xFE (full on), and ramp rate of 0x1F (.1sec).
setupLink=04.05.06,1, 01.02.03, 3, 0x80, 0x1F - sets up a link between device 01.02.03 as controller and 04.05.06 as responder with 50% on level and .1sec ramprate using 3 hops (to create the link, not to execute the link).

EXPECTED SDM IMPROVEMENTS
1. Global settings will be set and allocated per client.
2. More communication methods (setTextMode) will likely be added, such as object-oriented text, SOAP, XML, SNMP.
3. More interfaces to the SDM are expected, such as DLL, Command Line.
4. Sample code in many languages.
5. Strong dual-linking support.
6. More high-level use of new core functionality, such as timer, link and event management.
7. More use of PC+ resources.
8. Allow installation as a Windows Service.
INSTEON (MESSAGE) COMMANDS (hex)
<from> <to> <flags> <CMD1> <cmd2>
01-Assign to Group - Upon tap-tap linking, this instructs the destination device to add the source device id to its database. (Cmd2=group number).
02-Delete from Group - Upon tap-tap linking, this instructs the destination device to add the source device id to its database. (Cmd2=group number).
10-Ping - Ask the destination device to identify itself with a set-button-pressed broadcast.
11-ON - Go ON to the specified level (cmd2). In a broadcast group command, the cmd2 is ignored as it relies upon its internal at-link-time setting.
12-Fast ON - Go ON to the specified level (cmd2). In a broadcast group command, the cmd2 is ignored as it relies upon its internal at-link-time setting. Fast means it ignores ramp rate.
13-OFF - Go OFF.
14-Fast OFF - Go OFF, ignoring the ramp rate.
15-Bright - Brighten one step (of 32 between on and off).
16-Dim - Dim one step (of 32 between on and off).
17-Start Manual Change - Begin dimming or brightening until 18-Stop Manual Change received. Cmd2 is 1 for brightening, 0 for dimming.
18-Stop Manual Change - End dimming or brightening started with 17-Start Manual Change received.
19-Status Request - Request on-level status from a unit. Status is returned in Cmd 2. Status is On Level, Cmd 1 is a number that increments every time there is a change in the Receiving Devices Link Database (Database delta).
28-Set MSB for Peek/Poke - Set EEPROM Hi byte (MSB) for subsequent peek/pokes. Saved in RAM, therefore resetting resets this value.
29-Poke EE - Poke a previously peeked address with a single byte (cmd2). NOTE: Must use 28-Set MSB and 2B-Peek (for LSB) before poking data in order to set the location.
2A-Poke EE Extended - Poke a previously peeked address with a set of bytes. (Cmd2 is the LSB, Data1 is the number of bytes, Data2-Data14 are those bytes (up to 13 bytes)).
2B-Peek - Peek a single or group of bytes, also setting the LSB for future pokes, if necessary. MSB should be previously set with #28. Cmd2 is the LSB. If this is a standard INSTEON message, a single byte from that MSB/LSB location is returned in Cmd2. If this is an extended message, then 15 bytes are returned from Cmd2 to Data14.
REGISTRY SETTINGS
The Device Manager's root location is:
HKEY_CURRENT_USERSoftwareSmarthome SmarthomeDeviceManager
Valuename: port = <port> - sticky global port for Device Manager to connect - USB4 or COM1..COM255. Set from the port= command.
Valuename: servername = <exefilename> - Device Manager's executable for clients to autorun.
Valuename: usehttp = <true/false> - allow the http server to accept connections. (Defaults to true)
Valuename: httpport = <portnumber> - when the http server activates, the server uses this port (Defaults to 9020).

 

Jason Bauer

Written by

Jason Bauer is an owner and programmer for Portforward.com. He's allergic to twitter and facebook, but you can find more of his articles in the Guides section.
 
Friday, 20-Oct-2017 10:49:10 PDT