Notes {labels: 'NIL, viewFont: 10241} // nil=Unfiled, or specify a folder, e.g., 'Business. remove viewFont for current Styles default ERASE! remove initial // to erase existing entries in this folder first x10.nwt -- Slurpee format, also needs x10.bit (for a bitmap graphic) INCLUDED BELOW!!! 5/29/95 version 0.9 by William H. Ball (bball@mailstorm.dot.gov) Distributed under GNU CopyLeft. You may modify and distribute this code, but not claim it for your own. Newton owners with CP290 interfaces, Newt and a Newton are encouraged to improve, build, extract and distribute this app in .pkg format (I don't have NCK for the Mac or Windows). Description This is a small, floating app for the Apple Newton. By connecting an X-10 CP290 interface to the Newton or your favorite home computer, you can directly control up to 256 X-10 devices, including devices responding to dim commands (light control modules). For technical information concerning the CP290, see the CP290 Programming Manual. Generally, the interface provides: a real-time clock, storage for events, storage for graphics data, and transmission of control signals through the electrical wiring of your home. The interface accepts eight types of instructions, has a 5-pin DIN RS232 interface, and 2k x 8 RAM. Caveats This is my first attempt at translating an NTK project to Newt. Do not use this code as a good example of NewtonScript programming. There is currently one nasty bug: This is that X10 will crash your Newton if you hit the Send button without being connected to the CP290 interface (the serial handling routines are still a mystery to me). A good X10 app will read the acknowledge data from the interface following the sending of a command, store device icons and events, synch with your computer's clock, and offer graphical control of your devices. This version of the Newton X10 app simply sends commands to the interface. I believe there is a version of an X-10 controller with an IR interface (Radio Shack sells a universal remote with X-10 buttons), so it may be possible to convert this code for IR control through your Newton. Possible improvements would be: adding the ability to synch your Newton's clock with the CP290 or the CP290's clock with your Newton; ability to name or assign icons to devices; or code to view, change or enter device events. This would require serial reads and soup storage. Credits Thanks to Howard Oakley, EHN & DIJ Oakley for the floating code, Matthew Dixon Cowles for serial port code, and of course, Steve Weyer, for Newt, an amazing feat of programming. ---------- X10 //:doObj('build,'X10) //:doObj('add,'X10) //:doObj('remove,'X10) {_proto: protoFloatNGo, viewBounds: {left: -3, top: 181, right: 148, bottom: 268}, viewSetupFormScript: func() begin :setupIdle(10000); end, viewIdleScript: func() begin return 10000; end, viewFlags: 68, viewQuitScript: func() :setupIdle(0), viewClickScript: func(unit) begin :Drag(unit, nil); :Dirty(); end, // send the commands to the interface sendIt: // if shouldDoCr == TRUE, append CR/LF pair func(text,shouldDoCr) begin myEndpoint:output(text,NIL); if shouldDoCr then begin myEndpoint:output(unicodeCR,NIL); myEndpoint:output(unicodeLF,NIL); end; myEndpoint:flushOutput(); end, // kinda weird way to handle alerts... see the Newt manual X10Notify: func(msg1, msg2) GetRoot():Notify(3,EnsureInternal(msg1), EnsureInternal(msg2)), // misc declarations title: "X10 CP290 Controller", myEndpoint: nil, HouseCode: 96, lowUnitCode: 128, highUnitCode: 0, cmd: 2, declareSelf: 'base, // for close box // note: this requires you to slurp x10.bit before you build the app // this bitmap is used for the Extras app icon and a graphic on the floating windoid _package: {shortTitle: "X10", icon: :GetPictAsBits("Bitmaps", "X10Icon"), }, } ---------- X10+titleObj {// title at top (uses X10.title) _proto: protoTitle, } ---------- X10+logo {viewclass: clPictureView, // draws the X-10 icon over a hidden About button icon: :GetPictAsBits("Bitmaps","X10Icon"), viewFlags: 65, viewFormat: nil, viewBounds: {left: 6, top: 14, right: 50, bottom: 63}, viewclass: 76, } ---------- X10+pCommand {_proto: protoLabelPicker, viewBounds: {left: 53, top: 48, right: 151, bottom: 62}, text: "Command", labelCommands: ["on","off","90%","80%","70%","60%","50%","40%","30%","20%","10%"], textChanged: func() begin // translate commands to required values (upper nibble of byte 18) local lcmd := entryLine.text; if StrEqual(lcmd,"on") then cmd := 2; if StrEqual(lcmd,"off") then cmd := 3; if StrEqual(lcmd,"90%") then cmd := 21; if StrEqual(lcmd,"80%") then cmd := 53; if StrEqual(lcmd,"70%") then cmd := 85; if StrEqual(lcmd,"60%") then cmd := 101; if StrEqual(lcmd,"50%") then cmd := 117; if StrEqual(lcmd,"40%") then cmd := 133; if StrEqual(lcmd,"30%") then cmd := 165; if StrEqual(lcmd,"20%") then cmd := 197; if StrEqual(lcmd,"10%") then cmd := 229; end, } ---------- X10+pHouseCode {_proto: protoLabelPicker, viewBounds: {left: 53, top: 18, right: 151, bottom: 32}, labelCommands: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P"], text: "HouseCode ", textChanged: // fill in hex values for HouseCode according to picked letter // 16 different housecodes x 16 units = 256 different devices func() begin local letter := entryLine.text; if StrEqual(letter,"A") then HouseCode := 0x60; if StrEqual(letter,"B") then HouseCode := 0xE0; if StrEqual(letter,"C") then HouseCode := 0x20; if StrEqual(letter,"D") then HouseCode := 0xA0; if StrEqual(letter,"E") then HouseCode := 0x10; if StrEqual(letter,"F") then HouseCode := 0x90; if StrEqual(letter,"G") then HouseCode := 0x50; if StrEqual(letter,"H") then HouseCode := 0xD0; if StrEqual(letter,"I") then HouseCode := 0x70; if StrEqual(letter,"J") then HouseCode := 0xF0; if StrEqual(letter,"K") then HouseCode := 0x30; if StrEqual(letter,"L") then HouseCode := 0xB0; if StrEqual(letter,"M") then HouseCode := 0x00; if StrEqual(letter,"N") then HouseCode := 0x80; if StrEqual(letter,"O") then HouseCode := 0x40; if StrEqual(letter,"P") then HouseCode := 0xC0; end, } ---------- X10+Unitcode {_proto: protoLabelPicker, viewBounds: {left: 53, top: 33, right: 151, bottom: 47}, labelCommands: ["1","2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"], text: "UnitCode", textChanged: // fill in the values for lowUnitCode and highUnitCode according to picked unit // 16 different units x 16 housecodes = 256 different devices func() begin local unit := RIntToL(StringToNumber(entryLine.text)); if (unit = 1) then begin lowUnitCode := 128; highUnitCode := 0; end; if (unit = 2) then begin lowUnitCode := 64; highUnitCode := 0; end; if (unit = 3) then begin lowUnitCode := 32; highUnitCode := 0; end; if (unit = 4) then begin lowUnitCode := 16; highUnitCode := 0; end; if (unit = 5) then begin lowUnitCode := 8; highUnitCode := 0; end; if (unit = 6) then begin lowUnitCode := 4; highUnitCode := 0; end; if (unit = 7) then begin lowUnitCode := 2; highUnitCode := 0; end; if (unit = 8) then begin lowUnitCode := 1; highUnitCode := 0; end; if (unit = 9) then begin highUnitCode := 128; lowUnitCode := 0; end; if (unit = 10) then begin highUnitCode := 64; lowUnitCode := 0; end; if (unit = 11) then begin highUnitCode := 32; lowUnitCode := 0; end; if (unit = 12) then begin highUnitCode := 16; lowUnitCode := 0; end; if (unit = 13) then begin highUnitCode := 8; lowUnitCode := 0; end; if (unit = 14) then begin highUnitCode := 4; lowUnitCode := 0; end; if (unit = 15) then begin highUnitCode := 2; lowUnitCode := 0; end; if (unit = 16) then begin highUnitCode := 1; lowUnitCode := 0; end; end, } ---------- X10+Aboutbutton {_proto: protoTextButton, // this is hidden under the X-10 logo text: " ", viewBounds: {left: 10, top: 20, right: 41, bottom: 55}, viewFlags: 515, viewFormat: 67109376, buttonClickScript: func() begin :X10Notify("X-10 Newton Home Control","by bball@mailstorm.dot.gov Distributed under GNU CopyLeft Version 0.9 of 29May95, created with Newt3.1b by Steve Weyer. Source code available."); end, } ---------- X10+Questionbutton {_proto: protoTextButton, // some help, huh? text: "?", viewBounds: {left: 20, top: 71, right: 34, bottom: 84}, buttonClickScript: func() begin :X10Notify("X-10 Newton Home Control","Connect the CP290 interface, then select HouseCode, UnitCode and Command. Then press the Send button. Note: Dim commands only work with dimmable modules."); end, } ---------- X10+sendButton {_proto: protoTextButton, text: "SEND", // set up serial port for the CP290 interface protoSerialProtocol: // setup for X-10 requires 600 baud, 8N1 { _proto: protoEndpoint, // the basic serial endpoint with the serial port configuration configOptions: [ { label: kCMSAsyncSerial, type: 'service, opCode: opSetRequired }, { label: kCMOSerialIOParms, type: 'option, opCode: opSetNegotiate, data: { bps: k600bps, dataBits: k8DataBits, stopBits: k1StopBits, parity: kNoParity } }, { label: kCMOInputFlowControlParms, type: 'option, opCode: opSetNegotiate, data: { xonChar: unicodeDC1, xoffChar: unicodeDC3, useSoftFlowControl: true, useHardFlowControl: nil } }, ] }, // most of the work is done here... first, a buffer is created to hold the codes // to be sent to the CP290... then a synch buffer is built... next, a required // checksum byte is computer, and the command bytes are placed in the send buffer... // finally, the data is sent to CP290 from the Newton at an amazing speed of 600 baud! buttonClickScript: func() begin // decls for X-10 serial comms: 600 baud, 8N1 myEndpoint := {_proto: protoSerialProtocol, _parent: self}; // decls for synch buffer buffer := []; local buff_pos := 0; local CheckSum := 0; SetLength(buffer,22); // now assemble the synch buffer to be sent to the X-10 interface for buff_pos := 0 to 15 do buffer[buff_pos] := RIntToL(0xff); // placed your picked values into the buffer; the order is important buffer[16] := 1; buffer[17] := RIntToL(cmd); buffer[18] := RIntTol(HouseCode); buffer[19] := RIntToL(highUnitCode); buffer[20] := RIntToL(lowUnitCode); // now compute the required CheckSum for the X-10 interface buffer[21] := buffer[17] + buffer[18] + buffer[19] + buffer[20]; // start the connect -- this needs work! myEndpoint:Instantiate(myEndpoint,nil); myEndpoint:Connect(nil,nil); // now send the buffer a byte at a time to do the magic X-10 thang for buff_pos := 0 to 21 do :sendIt(buffer[buff_pos],nil); // here's where we really should wait for and read the CP290's // acknowledgement of the command (ACK of 6 0xff bytes, followed by // a '1' or a '0' for success or failure) // finish up myEndpoint:FlushOutput(); myEndpoint:SetInputSpec(NIL); myEndpoint:Abort(); AddDelayedAction( func() begin myEndpoint:Disconnect(); myEndpoint:Dispose(); end,[],2500); end, viewBounds: {left: 76, top: 72, right: 124, bottom: 83}, } ---------- BYE!