Multi device sync/backup
This commit is contained in:
commit
c9544a6421
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
9
.idea/homecontroller.iml
Normal file
9
.idea/homecontroller.iml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/homecontroller.iml" filepath="$PROJECT_DIR$/.idea/homecontroller.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
125
device.go
Normal file
125
device.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getNewID(clients []StoredClient) uint32 {
|
||||||
|
newID := uint32(0)
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.ID >= newID {
|
||||||
|
newID = client.ID + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newID
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDevice(name string, deviceType uint8, clients []StoredClient) (key []byte, usedID uint32) {
|
||||||
|
key = make([]byte, 64)
|
||||||
|
_, err := rand.Read(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
usedID = getNewID(clients)
|
||||||
|
storedClients = append(storedClients, StoredClient{
|
||||||
|
Key: key,
|
||||||
|
Name: name,
|
||||||
|
ID: usedID,
|
||||||
|
Type: deviceType,
|
||||||
|
Privileges: 0,
|
||||||
|
})
|
||||||
|
return key, usedID
|
||||||
|
}
|
||||||
|
|
||||||
|
func addController(name string, clients []StoredClient) (key []byte, usedID uint32) {
|
||||||
|
key = make([]byte, 64)
|
||||||
|
_, err := rand.Read(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
usedID = getNewID(clients)
|
||||||
|
storedClients = append(storedClients, StoredClient{
|
||||||
|
Key: key,
|
||||||
|
Name: name,
|
||||||
|
ID: usedID,
|
||||||
|
Type: 0,
|
||||||
|
Privileges: 1,
|
||||||
|
})
|
||||||
|
return key, usedID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) setChannel(id uint8, value uint8) error {
|
||||||
|
if client.channels == nil || uint8(len(client.channels)) >= id {
|
||||||
|
return errors.New("channel impossible to set")
|
||||||
|
}
|
||||||
|
var outBuffer []byte
|
||||||
|
outBuffer = append(outBuffer, id)
|
||||||
|
outBuffer = append(outBuffer, value)
|
||||||
|
|
||||||
|
return client.client.sendPacket(2, outBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) getDeviceChannelResponse(targetDeviceID uint32, id uint8) error {
|
||||||
|
if clients == nil || uint32(len(clients)) >= targetDeviceID {
|
||||||
|
return errors.New("target device is nonexistent")
|
||||||
|
}
|
||||||
|
clientDevice := clients[targetDeviceID]
|
||||||
|
if clientDevice.channels == nil || uint8(len(clientDevice.channels)) >= id {
|
||||||
|
return errors.New("channel impossible to get")
|
||||||
|
}
|
||||||
|
var outBuffer []byte
|
||||||
|
|
||||||
|
var deviceID []byte
|
||||||
|
binary.LittleEndian.PutUint32(deviceID, targetDeviceID)
|
||||||
|
outBuffer = append(outBuffer, deviceID...)
|
||||||
|
outBuffer = append(outBuffer, id)
|
||||||
|
outBuffer = append(outBuffer, clientDevice.channels[id])
|
||||||
|
|
||||||
|
return client.client.sendPacket(4, outBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) readChannel(id uint8) error {
|
||||||
|
if client.channels == nil || uint8(len(client.channels)) >= id {
|
||||||
|
return errors.New("channel impossible to get")
|
||||||
|
}
|
||||||
|
var outBuffer []byte
|
||||||
|
outBuffer = append(outBuffer, id)
|
||||||
|
|
||||||
|
return client.client.sendPacket(3, outBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) readChannels() error {
|
||||||
|
if client.channels == nil || uint8(len(client.channels)) == 0 {
|
||||||
|
return errors.New("channels impossible to get")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
for channelID := range client.channels {
|
||||||
|
err = client.readChannel(uint8(channelID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) receiveChannel(data []byte) error {
|
||||||
|
if len(data) < 1 {
|
||||||
|
return errors.New("channel data too short")
|
||||||
|
}
|
||||||
|
id := data[0]
|
||||||
|
if client.channels == nil || uint8(len(client.channels)) >= id {
|
||||||
|
return errors.New("channel impossible to get")
|
||||||
|
}
|
||||||
|
value := data[4:5][0]
|
||||||
|
client.channels[id] = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var deviceChannelCount = []uint8{0, 1, 1, 2}
|
||||||
|
|
||||||
|
//0: phone
|
||||||
|
//1: switch
|
||||||
|
//2: dimmer
|
||||||
|
//3: gate
|
44
dimmerDevice.go
Normal file
44
dimmerDevice.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type DimmerDevice struct {
|
||||||
|
baseDevice *Device
|
||||||
|
lastBrightness uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func createDimmerDevice(device *Device) *DimmerDevice {
|
||||||
|
device.typeID = 1
|
||||||
|
return &DimmerDevice{baseDevice: device}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dimmerDevice *DimmerDevice) on() error {
|
||||||
|
if dimmerDevice.baseDevice.channels[0] != 1 {
|
||||||
|
if dimmerDevice.lastBrightness == 0 {
|
||||||
|
dimmerDevice.lastBrightness = 255
|
||||||
|
}
|
||||||
|
return dimmerDevice.baseDevice.setChannel(0, dimmerDevice.lastBrightness)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dimmerDevice *DimmerDevice) off() error {
|
||||||
|
if dimmerDevice.baseDevice.channels[0] != 2 {
|
||||||
|
dimmerDevice.lastBrightness = dimmerDevice.baseDevice.channels[0]
|
||||||
|
return dimmerDevice.baseDevice.setChannel(0, 1)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dimmerDevice *DimmerDevice) getStringState() string {
|
||||||
|
if dimmerDevice.baseDevice.channels[0] == 1 {
|
||||||
|
return "OFF"
|
||||||
|
} else if dimmerDevice.baseDevice.channels[0] > 1 {
|
||||||
|
percentVal := dimmerDevice.baseDevice.channels[0] / 255
|
||||||
|
return fmt.Sprintf("ON at %d%%", percentVal)
|
||||||
|
}
|
||||||
|
return "Unknown"
|
||||||
|
}
|
48
gateDevice.go
Normal file
48
gateDevice.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type GateDevice struct {
|
||||||
|
baseDevice *Device
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func createGateDevice(device *Device) *GateDevice {
|
||||||
|
device.typeID = 3
|
||||||
|
return &GateDevice{baseDevice: device}
|
||||||
|
}
|
||||||
|
|
||||||
|
//states:
|
||||||
|
//0: unknown
|
||||||
|
//1: closed
|
||||||
|
//2: closing
|
||||||
|
//3: opening
|
||||||
|
//4: open
|
||||||
|
|
||||||
|
func (gateDevice *GateDevice) open() error {
|
||||||
|
if gateDevice.baseDevice.channels[0] != 3 && gateDevice.baseDevice.channels[1] != 4 {
|
||||||
|
return gateDevice.baseDevice.setChannel(0, 3)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gateDevice *GateDevice) close() error {
|
||||||
|
if gateDevice.baseDevice.channels[0] != 1 && gateDevice.baseDevice.channels[1] != 2 {
|
||||||
|
return gateDevice.baseDevice.setChannel(0, 2)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gateDevice *GateDevice) getStringState() string {
|
||||||
|
switch gateDevice.baseDevice.channels[0] {
|
||||||
|
case 1:
|
||||||
|
return "Closed"
|
||||||
|
case 2:
|
||||||
|
return "Closing"
|
||||||
|
case 3:
|
||||||
|
return "Opening"
|
||||||
|
case 4:
|
||||||
|
return "Open"
|
||||||
|
}
|
||||||
|
return "Unknown"
|
||||||
|
}
|
3
go.mod
Normal file
3
go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module homecontroller
|
||||||
|
|
||||||
|
go 1.23.0
|
0
go.sum
Normal file
0
go.sum
Normal file
54
main.go
Normal file
54
main.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var clients []*Device
|
||||||
|
var storedClients []StoredClient
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
clients = make([]*Device, 0)
|
||||||
|
data, err := os.ReadFile("clients.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(data, &storedClients)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a connection to the server
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error resolving address " + err.Error())
|
||||||
|
}
|
||||||
|
listen, err := net.ListenTCP("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
// close listener
|
||||||
|
defer func(listen *net.TCPListener) {
|
||||||
|
_ = listen.Close()
|
||||||
|
}(listen)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := listen.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
var opErr *net.OpError
|
||||||
|
if errors.As(err, &opErr) && opErr.Err.Error() == "use of closed network connection" {
|
||||||
|
log.Println("Listener closed, stopping server.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Error accepting connection:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleRequest(conn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
231
netCode.go
Normal file
231
netCode.go
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"hash/crc32"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
cipher *cipher.Block
|
||||||
|
conn *net.TCPConn
|
||||||
|
device *Device
|
||||||
|
name string
|
||||||
|
id uint32
|
||||||
|
typeID uint8
|
||||||
|
channels []uint8
|
||||||
|
client *Device
|
||||||
|
lastHeardFrom int64
|
||||||
|
privileges uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type StoredClient struct {
|
||||||
|
Key []byte `json:"key"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID uint32 `json:"id"`
|
||||||
|
Type uint8 `json:"type"`
|
||||||
|
Privileges uint8 `json:"privileges"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendPacket sends a packet with the given packetID and data over the provided TCP connection.
|
||||||
|
func sendPacket(packetID uint8, data []byte, client *Device) error {
|
||||||
|
// Create a buffer with the packet ID and data
|
||||||
|
tempBuffer := append([]byte{packetID}, data...)
|
||||||
|
|
||||||
|
// Calculate the checksum of the packet ID + data
|
||||||
|
checksum := crc32.ChecksumIEEE(tempBuffer)
|
||||||
|
|
||||||
|
// Convert the checksum to a 4-byte slice
|
||||||
|
checksumBytes := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(checksumBytes, checksum)
|
||||||
|
|
||||||
|
var encryptedTempBuffer []byte
|
||||||
|
|
||||||
|
if client.cipher != nil {
|
||||||
|
(*client.cipher).Encrypt(encryptedTempBuffer, tempBuffer)
|
||||||
|
} else {
|
||||||
|
encryptedTempBuffer = tempBuffer
|
||||||
|
}
|
||||||
|
// Calculate the total length (checksum + packet ID + data)
|
||||||
|
packetLength := uint32(len(encryptedTempBuffer) + 4)
|
||||||
|
|
||||||
|
// Prepare the output buffer with the length field
|
||||||
|
outBuffer := make([]byte, 4)
|
||||||
|
|
||||||
|
binary.LittleEndian.PutUint32(outBuffer, packetLength)
|
||||||
|
// Append the tempBuffer (checksum + packet ID + data) to outBuffer
|
||||||
|
outBuffer = append(outBuffer, checksumBytes...)
|
||||||
|
outBuffer = append(outBuffer, tempBuffer...)
|
||||||
|
|
||||||
|
// Send the packet over the connection
|
||||||
|
writeLen, err := client.conn.Write(outBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if writeLen != len(outBuffer) {
|
||||||
|
return errors.New("write length does not match output buffer length")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) sendPacket(packetID uint8, data []byte) error {
|
||||||
|
return sendPacket(packetID, data, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) receivePacket() (uint8, uint32, []byte, error) {
|
||||||
|
return receivePacket(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateLastSeen(client *Device) {
|
||||||
|
if client.conn != nil {
|
||||||
|
client.lastHeardFrom = time.Now().Unix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// receivePacket receives a packet from the provided TCP connection, decrypts it, and verifies it.
|
||||||
|
func receivePacket(client *Device) (uint8, uint32, []byte, error) {
|
||||||
|
// Step 1: Read the packet length (4 bytes)
|
||||||
|
lengthBuffer := make([]byte, 4)
|
||||||
|
n, err := client.conn.Read(lengthBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, nil, err
|
||||||
|
}
|
||||||
|
if n != len(lengthBuffer) {
|
||||||
|
return 0, 0, nil, errors.New("read length does not match length buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
packetLength := binary.LittleEndian.Uint32(lengthBuffer)
|
||||||
|
|
||||||
|
// Step 2: Allocate a buffer to hold the rest of the packet (checksum + packetID + data)
|
||||||
|
packetBuffer := make([]byte, packetLength)
|
||||||
|
n, err = client.conn.Read(packetBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, nil, err
|
||||||
|
}
|
||||||
|
if n != int(packetLength) {
|
||||||
|
return 0, 0, nil, errors.New("read length does not match packet length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Decrypt the packet buffer
|
||||||
|
decryptedPacketBuffer := make([]byte, len(packetBuffer))
|
||||||
|
if client.cipher != nil {
|
||||||
|
(*client.cipher).Decrypt(decryptedPacketBuffer, packetBuffer)
|
||||||
|
} else {
|
||||||
|
decryptedPacketBuffer = packetBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Extract the checksum (4 bytes)
|
||||||
|
receivedChecksum := binary.LittleEndian.Uint32(decryptedPacketBuffer[:4])
|
||||||
|
|
||||||
|
// Step 5: Extract the packet ID (1 byte)
|
||||||
|
packetID := decryptedPacketBuffer[4]
|
||||||
|
|
||||||
|
// Step 6: Extract the data
|
||||||
|
data := decryptedPacketBuffer[5:]
|
||||||
|
|
||||||
|
// Step 7: Verify the checksum
|
||||||
|
calculatedChecksum := crc32.ChecksumIEEE(append([]byte{packetID}, data...))
|
||||||
|
if receivedChecksum != calculatedChecksum {
|
||||||
|
return 0, 0, nil, errors.New("checksum mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the packet ID and data
|
||||||
|
updateLastSeen(client)
|
||||||
|
return packetID, uint32(len(data)), data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) receiveConnectionRequest(data []byte) {
|
||||||
|
if len(data) < 4 {
|
||||||
|
clientID := binary.LittleEndian.Uint32(data[0:4])
|
||||||
|
for _, clientLoop := range storedClients {
|
||||||
|
if clientLoop.ID == clientID {
|
||||||
|
client.name = clientLoop.Name
|
||||||
|
client.id = clientID
|
||||||
|
if clientLoop.Privileges > 0 {
|
||||||
|
clientLoop.Type = 0
|
||||||
|
}
|
||||||
|
client.device = &Device{
|
||||||
|
typeID: clientLoop.Type,
|
||||||
|
channels: make([]uint8, deviceChannelCount[clientLoop.Type]),
|
||||||
|
client: client,
|
||||||
|
privileges: clientLoop.Privileges,
|
||||||
|
}
|
||||||
|
clientCipher, err := aes.NewCipher(clientLoop.Key)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error creating AES cipher")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.cipher = &clientCipher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Device) receiveChannelUpdate(data []byte) {
|
||||||
|
if len(data) < 2 && len(client.channels) != 0 {
|
||||||
|
channelID := data[0]
|
||||||
|
channelValue := data[1]
|
||||||
|
if channelID >= uint8(len(client.channels)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.channels[channelID] = channelValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRequest(conn *net.TCPConn) {
|
||||||
|
//serverbound packets:
|
||||||
|
//0: start connection
|
||||||
|
//1: reserved
|
||||||
|
//2: channel value
|
||||||
|
//3: list devices
|
||||||
|
//4: set device channel
|
||||||
|
//5: get device channel
|
||||||
|
//6: list devices
|
||||||
|
//7: list admins
|
||||||
|
//8: add device
|
||||||
|
//9: add controller
|
||||||
|
|
||||||
|
//clientbound packets:
|
||||||
|
//2: set channel
|
||||||
|
//3: get channel
|
||||||
|
//4: channel from device
|
||||||
|
//5: added device key and id
|
||||||
|
//6: device entry
|
||||||
|
//7: admin entry
|
||||||
|
|
||||||
|
client := Device{
|
||||||
|
cipher: nil,
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
clients = append(clients, &client)
|
||||||
|
defer func(conn *net.TCPConn) {
|
||||||
|
_ = conn.Close()
|
||||||
|
}(conn)
|
||||||
|
for {
|
||||||
|
packetID, _, packetData, err := receivePacket(&client)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch packetID {
|
||||||
|
case 0:
|
||||||
|
if client.cipher == nil {
|
||||||
|
client.receiveConnectionRequest(packetData)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if client.cipher != nil {
|
||||||
|
client.receiveChannelUpdate(packetData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
switchDevice.go
Normal file
42
switchDevice.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type SwitchDevice struct {
|
||||||
|
baseDevice *Device
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func createSwitchDevice(device *Device) *SwitchDevice {
|
||||||
|
device.typeID = 1
|
||||||
|
return &SwitchDevice{baseDevice: device}
|
||||||
|
}
|
||||||
|
|
||||||
|
//states:
|
||||||
|
//0: unknown
|
||||||
|
//1: on
|
||||||
|
//2: off
|
||||||
|
|
||||||
|
func (switchDevice *SwitchDevice) on() error {
|
||||||
|
if switchDevice.baseDevice.channels[0] != 1 {
|
||||||
|
return switchDevice.baseDevice.setChannel(0, 1)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (switchDevice *SwitchDevice) off() error {
|
||||||
|
if switchDevice.baseDevice.channels[0] != 2 {
|
||||||
|
return switchDevice.baseDevice.setChannel(0, 2)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (switchDevice *SwitchDevice) getStringState() string {
|
||||||
|
switch switchDevice.baseDevice.channels[0] {
|
||||||
|
case 1:
|
||||||
|
return "On"
|
||||||
|
case 2:
|
||||||
|
return "Off"
|
||||||
|
}
|
||||||
|
return "Unknown"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user