|
|
using Application.Domain.Entities;
|
|
|
using Application.Models;
|
|
|
using Infrastructure.Data;
|
|
|
using Infrastructure.Extensions;
|
|
|
using IoT.Shared.Services;
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
using System;
|
|
|
using System.Collections.Concurrent;
|
|
|
using System.Collections.Generic;
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
using System.Diagnostics;
|
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Net;
|
|
|
using System.Net.NetworkInformation;
|
|
|
using System.Net.Sockets;
|
|
|
using System.Text;
|
|
|
using System.Text.RegularExpressions;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
namespace IoTNode.DeviceServices.FBee
|
|
|
{
|
|
|
public class FBeeService : BaseDeviceService
|
|
|
{
|
|
|
private readonly ConcurrentDictionary<string, TcpClientWrapper> Clients = new ConcurrentDictionary<string, TcpClientWrapper>();
|
|
|
private readonly ILogger<FBeeService> _logger;
|
|
|
|
|
|
public FBeeService(IServiceProvider applicationServices, ILogger<FBeeService> logger)
|
|
|
: base(applicationServices)
|
|
|
{
|
|
|
this._logger = logger;
|
|
|
}
|
|
|
|
|
|
public override Task StartAsync(CancellationToken cancellationToken)
|
|
|
{
|
|
|
Task.Run(async () =>
|
|
|
{
|
|
|
while (!cancellationToken.IsCancellationRequested)
|
|
|
{
|
|
|
foreach (var item in this.Clients)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
this._logger.LogInformation($"check gateway:{item.Key}");
|
|
|
this.X9d(item.Key);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
await Task.Delay(1000 * 60);
|
|
|
}
|
|
|
});
|
|
|
return base.StartAsync(cancellationToken);
|
|
|
}
|
|
|
|
|
|
public void UpdateButtons(string number, string buttons)
|
|
|
{
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = deviceRepo.Table().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
|
|
|
if (device != null)
|
|
|
{
|
|
|
var data = device.Data.FirstOrDefault(o => o.Key == Keys.Buttons);
|
|
|
if (data != null)
|
|
|
{
|
|
|
data.Value = buttons.FromJson<List<ButtonModel>>().ToJson(true);
|
|
|
deviceRepo.SaveChanges();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void IRMath(string number, byte type)
|
|
|
{
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = deviceRepo.Table().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
|
|
|
if (device != null)
|
|
|
{
|
|
|
var values = number.Split('-');
|
|
|
var version = device.Data.FirstOrDefault(o => o.Key == Keys.Version)?.Value;
|
|
|
if (version != null)
|
|
|
{
|
|
|
this.XA70081(values[0], values[1], version, type);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public override void Execute()
|
|
|
{
|
|
|
var ips = NetworkInterface.GetAllNetworkInterfaces()
|
|
|
.Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
|
|
.Select(nic => nic.GetIPProperties().UnicastAddresses)
|
|
|
.SelectMany(o => o.ToList())
|
|
|
.Select(o => o.Address)
|
|
|
.Where(o => o.AddressFamily == AddressFamily.InterNetwork)
|
|
|
.ToList();
|
|
|
var list = new ConcurrentBag<string>();
|
|
|
foreach (var ip in ips)
|
|
|
{
|
|
|
this._logger.LogDebug($"search:{ip}");
|
|
|
var tokenSource = new CancellationTokenSource();
|
|
|
using var client = new UdpClient(new IPEndPoint(ip, 0));
|
|
|
var message = Encoding.ASCII.GetBytes("GETIP\r\n");
|
|
|
client.Send(message, message.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 9090));
|
|
|
Task.Run(() =>
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var ep = new IPEndPoint(IPAddress.Any, 3702);
|
|
|
while (!tokenSource.IsCancellationRequested)
|
|
|
{
|
|
|
if (client.Available > 0)
|
|
|
{
|
|
|
var buffer = client.Receive(ref ep);
|
|
|
var result = Encoding.ASCII.GetString(buffer);
|
|
|
this._logger.LogDebug(result);
|
|
|
if (!list.Any(o => o == result))
|
|
|
{
|
|
|
list.Add(result);
|
|
|
}
|
|
|
}
|
|
|
Thread.Sleep(1);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}, tokenSource.Token);
|
|
|
Thread.Sleep(1000);
|
|
|
tokenSource.Cancel();
|
|
|
}
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var iotNodeClient = scope.ServiceProvider.GetService<IoTNodeClient>();
|
|
|
var categoryRepo = scope.ServiceProvider.GetService<IRepository<Category>>();
|
|
|
var productNumber = "fbee:gateway";
|
|
|
|
|
|
var product = this.UpdateProduct("FBee网关", productNumber, "/Gateway/", "00", "gateway");
|
|
|
var deviceNodeRepo = scope.ServiceProvider.GetService<IRepository<Node>>();
|
|
|
var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
foreach (var result in list)
|
|
|
{
|
|
|
var sn = Regex.Match(result, @"SN:([^\s]*)").Groups[1].Value;
|
|
|
var ip = Regex.Match(result, @"IP:([^\s]*)").Groups[1].Value;
|
|
|
var writeList = GetSetting("fbee.writelist");
|
|
|
if (!string.IsNullOrEmpty(writeList))
|
|
|
{
|
|
|
var snList = writeList.Split(',');
|
|
|
if (!snList.Contains(sn))
|
|
|
{
|
|
|
this._logger.LogDebug($"skip {sn}:{ip} because it isn't contains by writelist");
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
var device = deviceRepo.Table().FirstOrDefault(o => o.Number == sn);
|
|
|
if (device == null)
|
|
|
{
|
|
|
device = new Device
|
|
|
{
|
|
|
Number = sn,
|
|
|
Name = "网关",
|
|
|
DisplayName = "网关",
|
|
|
Icon = "gateway",
|
|
|
ProductId = product.Id,
|
|
|
NodeId = node.Id,
|
|
|
};
|
|
|
deviceRepo.Add(device);
|
|
|
}
|
|
|
device.Ip = ip;
|
|
|
deviceRepo.SaveChanges();
|
|
|
}
|
|
|
var gateways = deviceRepo.ReadOnlyTable().Where(o => o.Product.Name == "FBee网关").ToList();
|
|
|
foreach (var gateway in gateways)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
if (Clients.Any(o => o.Key == gateway.Number))
|
|
|
{
|
|
|
Clients.TryGetValue(gateway.Number, out TcpClientWrapper client);
|
|
|
if (client.Ip != gateway.Ip)
|
|
|
{
|
|
|
client.Ip = gateway.Ip;
|
|
|
this.Connect(client);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (!client.Client.Connected)
|
|
|
{
|
|
|
this.Connect(client);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
var client = new TcpClientWrapper { Sn = gateway.Number, Ip = gateway.Ip, Client = new TcpClient() };
|
|
|
Clients.TryAdd(gateway.Number, client);
|
|
|
this.Connect(client);
|
|
|
}
|
|
|
this.X81(gateway.Number);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
foreach (var item in Clients)
|
|
|
{
|
|
|
if (!gateways.Any(o => o.Number == item.Key))
|
|
|
{
|
|
|
Clients.Remove(item.Key, out TcpClientWrapper value);
|
|
|
value.Client.Dispose();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public override Task StopAsync(CancellationToken cancellationToken)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
foreach (var item in Clients)
|
|
|
{
|
|
|
if (item.Value.Client != null)
|
|
|
{
|
|
|
item.Value.Client.Dispose();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
this._logger.LogDebug($"{this.GetType().Name} Service Stopd");
|
|
|
}
|
|
|
return Task.CompletedTask;
|
|
|
}
|
|
|
|
|
|
#region private
|
|
|
|
|
|
private void Connect(TcpClientWrapper client)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
if (client != null)
|
|
|
{
|
|
|
client.Client.Dispose();
|
|
|
client.Client = new TcpClient();
|
|
|
}
|
|
|
this._logger.LogDebug($"connect {client.Ip} for {client.Sn}");
|
|
|
client.Client.Connect(client.Ip, 8001);
|
|
|
new Thread(() =>
|
|
|
{
|
|
|
var stream = client.Client.GetStream();
|
|
|
while (client.Client.Connected)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
if (stream.DataAvailable)
|
|
|
{
|
|
|
var buffer = new byte[512];
|
|
|
var length = stream.Read(buffer);
|
|
|
var data = buffer.ToList().Take(length).ToArray();
|
|
|
this._logger.LogDebug($"response from {client.Sn}:{BitConverter.ToString(data)}");
|
|
|
this.Handle(client.Sn, data);
|
|
|
}
|
|
|
Thread.Sleep(1);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack($"read stream {client.Sn} error");
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}).Start();
|
|
|
this.X9d(client.Sn);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Handle(string sn, byte[] data)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var length = 2 + data[1];
|
|
|
if (data.Length > length)
|
|
|
{
|
|
|
Handle(sn, data.Skip(length).ToArray());
|
|
|
}
|
|
|
this.HandleInternal(sn, data.Take(length).ToArray());
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void HandleInternal(string sn, byte[] data)
|
|
|
{
|
|
|
this._logger.LogDebug($"read from {sn}:{BitConverter.ToString(data).Replace("-", " ")}");
|
|
|
var responseType = data[0];
|
|
|
if (responseType == ResponseType.x15)
|
|
|
{
|
|
|
this.X15(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x01)
|
|
|
{
|
|
|
this.X01(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x07)
|
|
|
{
|
|
|
this.X07(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x08)
|
|
|
{
|
|
|
this.X08(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x09)
|
|
|
{
|
|
|
this.X09(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x0a)
|
|
|
{
|
|
|
this.X0A(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x27)
|
|
|
{
|
|
|
this.X27(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x70)
|
|
|
{
|
|
|
this.X70(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x72)
|
|
|
{
|
|
|
this.X70(sn, data);
|
|
|
}
|
|
|
else if (responseType == ResponseType.x75)
|
|
|
{
|
|
|
//网关应答指令
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"{responseType} hasn't handle");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Write(string sn, byte commandType, string ieee = null, List<byte> command = null, int format = 1, byte? ep = null)
|
|
|
{
|
|
|
if (ieee != null && ieee.Contains('-'))
|
|
|
{
|
|
|
ieee = ieee.Split('-')[1];
|
|
|
}
|
|
|
this.Clients.TryGetValue(sn, out TcpClientWrapper client);
|
|
|
var data = new List<byte>();
|
|
|
data.AddRange(sn.HexToBytes().Reverse());//sn号
|
|
|
data.Add(RequestType.xfe);//控制标志
|
|
|
data.Add(commandType);//控制类型
|
|
|
|
|
|
if (command != null)
|
|
|
{
|
|
|
byte addressType = 0x02;
|
|
|
var payload = new List<byte>();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDevice(deviceRepo, sn, ieee);
|
|
|
var address = device.GetDataValue(Keys.Address).HexToBytes().Reverse();
|
|
|
var endPoint = Convert.ToInt32(device.GetDataValue(Keys.EndPoint));
|
|
|
if (format == 0)
|
|
|
{
|
|
|
payload.Add((byte)(command.Count() + 1 + 2));//数据总长
|
|
|
payload.Add(addressType);//地址模式
|
|
|
payload.AddRange(address);
|
|
|
payload.AddRange(command);
|
|
|
}
|
|
|
else if (format == 1)
|
|
|
{
|
|
|
payload.Add((byte)(command.Count() + 1 + 2 + 6 + 1 + 2));//数据总长
|
|
|
payload.Add(addressType);//地址模式
|
|
|
payload.AddRange(address);
|
|
|
payload.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
|
|
|
payload.Add(ep ?? (byte)endPoint);
|
|
|
payload.AddRange(new byte[] { 0x00, 0x00 });
|
|
|
payload.AddRange(command);
|
|
|
}
|
|
|
else if (format == 2)
|
|
|
{
|
|
|
payload.Add((byte)(command.Count() + 2 + 1));//数据总长
|
|
|
payload.AddRange(address);
|
|
|
payload.Add(ep ?? (byte)endPoint);
|
|
|
payload.AddRange(command);
|
|
|
}
|
|
|
data.AddRange(payload);
|
|
|
}
|
|
|
|
|
|
var MessageLength = BitConverter.GetBytes((ushort)(data.Count() + 2));//消息总长
|
|
|
if (BitConverter.IsLittleEndian)
|
|
|
{
|
|
|
MessageLength.Reverse();
|
|
|
}
|
|
|
var message = new List<byte>();
|
|
|
message.AddRange(MessageLength);
|
|
|
message.AddRange(data);
|
|
|
var bytes = message.ToArray();
|
|
|
this.Send(client, bytes);
|
|
|
}
|
|
|
|
|
|
private void Send(TcpClientWrapper client, byte[] bytes)
|
|
|
{
|
|
|
this._logger.LogDebug($"write:{BitConverter.ToString(bytes).Replace("-", " ")}");
|
|
|
try
|
|
|
{
|
|
|
client?.Client.GetStream().Write(bytes);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
if (!client.Client.Connected)
|
|
|
{
|
|
|
this.Connect(client);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion private
|
|
|
|
|
|
#region api
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x81获取当前连接所有设备
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
public void X81(string sn)
|
|
|
{
|
|
|
this._logger.LogDebug($"refresh {sn}");
|
|
|
this.Write(sn, RequestType.x81);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 接收当前连接的设备信息返回值
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X01(string sn, byte[] data)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var iotNodeClient = scope.ServiceProvider.GetService<IoTNodeClient>();
|
|
|
var profileId = ms.ReadInt();
|
|
|
var deviceId = ms.ReadInt();
|
|
|
if (deviceId == 0)
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
var switchState = ms.ReadByte();
|
|
|
var nameLength = ms.ReadByte();
|
|
|
var cname = "";
|
|
|
if (nameLength > 0)
|
|
|
{
|
|
|
cname = ms.ReadASIIString(nameLength);
|
|
|
}
|
|
|
var isOnline = ms.ReadByte();
|
|
|
var ieee = ms.ReadHexString(8);
|
|
|
var snLength = ms.ReadByte();
|
|
|
if (snLength > 0)
|
|
|
{
|
|
|
var rawSn = ms.ReadHexString(snLength);
|
|
|
}
|
|
|
var zoneType = ms.ReadInt();
|
|
|
var deviceType = DeviceId.List.FirstOrDefault(o => o.RawDeviceId == deviceId);
|
|
|
var deviceIcon = deviceType.Icon;
|
|
|
if (deviceType != null)
|
|
|
{
|
|
|
var deviceName = deviceType.Name;
|
|
|
if (deviceId == 0x402)
|
|
|
{
|
|
|
if (zoneType == 0x0028)
|
|
|
{
|
|
|
deviceName = "烟雾报警器";
|
|
|
deviceIcon = "smoke";
|
|
|
}
|
|
|
else if (zoneType == 0x000d)
|
|
|
{
|
|
|
deviceName = "人体感应器";
|
|
|
deviceIcon = "infrared";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
deviceName = null;
|
|
|
}
|
|
|
}
|
|
|
else if (deviceId == 0x002)
|
|
|
{
|
|
|
if (zoneType == 0x0001)
|
|
|
{
|
|
|
deviceName = "一路开关";
|
|
|
deviceIcon = "switch1";
|
|
|
}
|
|
|
else if (zoneType == 0x0002)
|
|
|
{
|
|
|
deviceName = "二路开关";
|
|
|
deviceIcon = "switch2";
|
|
|
}
|
|
|
else if (zoneType == 0x0003)
|
|
|
{
|
|
|
deviceName = "三路开关";
|
|
|
deviceIcon = "switch3";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
deviceName = null;
|
|
|
}
|
|
|
}
|
|
|
if (string.IsNullOrEmpty(deviceName))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
var productNumber = $"fbee:{deviceType.RawDeviceId:x4}:{zoneType:x2}";
|
|
|
var number = deviceType.RawDeviceId;
|
|
|
var categoryRepo = scope.ServiceProvider.GetService<IRepository<Category>>();
|
|
|
var category = categoryRepo.ReadOnlyTable().FirstOrDefault(o => o.Number == deviceType.CategoryNumber);
|
|
|
|
|
|
var productRepo = scope.ServiceProvider.GetService<IRepository<Product>>();
|
|
|
var product = this.UpdateProduct(deviceName, productNumber, this.GetPathByDeviceName(deviceName), deviceType.CategoryNumber, deviceIcon);
|
|
|
|
|
|
var deviceNodeRepo = scope.ServiceProvider.GetService<IRepository<Node>>();
|
|
|
var node = deviceNodeRepo.ReadOnlyTable().FirstOrDefault();
|
|
|
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDevice(deviceRepo, sn, ieee);
|
|
|
var online = isOnline != 0x00;
|
|
|
if (device == null)
|
|
|
{
|
|
|
device = new Device
|
|
|
{
|
|
|
Number = $"{sn}-{ieee}",
|
|
|
Name = deviceName,
|
|
|
DisplayName = deviceName,
|
|
|
Icon = deviceIcon,
|
|
|
Gateway = sn,
|
|
|
ProductId = product.Id,
|
|
|
NodeId = node.Id
|
|
|
};
|
|
|
deviceRepo.Add(device);
|
|
|
device.IsOnline = online;
|
|
|
deviceRepo.SaveChanges();
|
|
|
if (device.Name == "红外转发器")
|
|
|
{
|
|
|
var buttons = new List<ButtonModel>() { new ButtonModel { Name = "测试", Value = "603", Order = 0 } }.ToJson(true);
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Buttons, buttons, DeviceDataType.String, "指令", timestamp: timestamp));
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
device.IsOnline = online;
|
|
|
deviceRepo.SaveChanges();
|
|
|
}
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.DeviceId, deviceId, DeviceDataType.Int, Keys.DeviceId, hidden: true, timestamp: timestamp));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Address, address, DeviceDataType.String, Keys.Address, hidden: true, timestamp: timestamp));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.EndPoint, endpoint, DeviceDataType.Int, Keys.EndPoint, hidden: true, timestamp: timestamp));
|
|
|
if (new int[] { 0x0002, 0x0009, 0x0081, 0x0202, 0x0220, 0x0051 }.Contains(deviceId))
|
|
|
{
|
|
|
if (deviceId == 0x202)
|
|
|
{
|
|
|
}
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.State, switchState == 0 ? "关" : (switchState == 1 ? "开" : "停"), DeviceDataType.String, "状态", timestamp: timestamp));
|
|
|
}
|
|
|
var battery = ms.ReadByte();
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Battery, battery, DeviceDataType.Int, Keys.Battery, hidden: true, timestamp: timestamp));
|
|
|
var epCount = ms.ReadByte();
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.EndPointCount, battery, DeviceDataType.Int, Keys.EndPointCount, hidden: true, timestamp: timestamp));
|
|
|
|
|
|
if (device.Name == "光强检测器")
|
|
|
{
|
|
|
ms.Read(2);
|
|
|
var light = BitConverter.ToInt16(ms.Read(2));
|
|
|
var desc = this.GetDescription(Keys.Light, light);
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Light, light, DeviceDataType.Int, "光照度", "lux", desc, timestamp: timestamp));
|
|
|
}
|
|
|
else if (device.Name == "温湿度传感器")
|
|
|
{
|
|
|
var temperature = BitConverter.ToInt16(ms.Read(2)) / 100f;
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Temperature, temperature, DeviceDataType.Float, "温度", "℃", this.GetDescription(Keys.Temperature, temperature), timestamp: timestamp));
|
|
|
|
|
|
var humidity = BitConverter.ToInt16(ms.Read(2)) / 100f;
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Humidity, humidity, DeviceDataType.Float, "湿度", "RH%", this.GetDescription(Keys.Humidity, humidity), timestamp: timestamp));
|
|
|
}
|
|
|
else if (device.Name == "烟雾报警器" || device.Name == "人体感应器")
|
|
|
{
|
|
|
var state = BitConverter.ToInt16(ms.Read(2));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Warning, state == 1 ? "警报" : "正常", DeviceDataType.String, "状态", timestamp: timestamp));
|
|
|
}
|
|
|
//var historyData = ms.ReadHexString((int)(ms.Length - ms.Position));
|
|
|
//device.AddorUpdateData(device.CreateData(Keys.Data, historyData, DeviceDataType.String, Keys.Data, hidden: true));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.ZoneType, zoneType, DeviceDataType.String, Keys.ZoneType, hidden: true, timestamp: timestamp));
|
|
|
//deviceRepo.SaveChanges();
|
|
|
//var deviceDto = device.To<EditDeviceModel>();
|
|
|
//this.SendToServer(Methods.EditDevice, deviceDto);
|
|
|
if (deviceId == 0x00a)
|
|
|
{
|
|
|
}
|
|
|
this.UpdateStatus(device);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"unknown device id :{deviceId}");
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private string GetPathByDeviceName(string deviceName)
|
|
|
{
|
|
|
if (deviceName == "插座")
|
|
|
{
|
|
|
return "/Socket/";
|
|
|
}
|
|
|
else if (deviceName == "智能插座")
|
|
|
{
|
|
|
return "/Socket/";
|
|
|
}
|
|
|
else if (deviceName == "一路开关")
|
|
|
{
|
|
|
return "/Switch/";
|
|
|
}
|
|
|
else if (deviceName == "二路开关")
|
|
|
{
|
|
|
return "/Switch2/";
|
|
|
}
|
|
|
else if (deviceName == "三路开关")
|
|
|
{
|
|
|
return "/Switch3/";
|
|
|
}
|
|
|
else if (deviceName == "窗帘电机")
|
|
|
{
|
|
|
return "/Curtain/";
|
|
|
}
|
|
|
else if (deviceName == "调色灯")
|
|
|
{
|
|
|
return "/ColorLight/";
|
|
|
}
|
|
|
else if (deviceName == "色暖灯")
|
|
|
{
|
|
|
return "/WarmLight/";
|
|
|
}
|
|
|
else if (deviceName == "红外转发器")
|
|
|
{
|
|
|
return "/Ir/";
|
|
|
}
|
|
|
else if (deviceName == "门锁")
|
|
|
{
|
|
|
return "/Door/";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void UpdateStatus(Device device)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var deviceId = Convert.ToInt32(device.GetDataValue(Keys.DeviceId));
|
|
|
var sn = device.Gateway;
|
|
|
var ieee = device.Number;
|
|
|
if (new int[] { 0x0002, 0x0009, 0x0081, 0x0202, 0x0220, 0x0051 }.Contains(deviceId))
|
|
|
{
|
|
|
//this.X85(sn, ieee);
|
|
|
}
|
|
|
if (deviceId == 0x0210 || deviceId == 0x0220)
|
|
|
{
|
|
|
this.X86(sn, ieee);
|
|
|
this.X87(sn, ieee);
|
|
|
this.X88(sn, ieee);
|
|
|
this.XA9(sn, ieee);
|
|
|
}
|
|
|
else if (deviceId == 0x0163)
|
|
|
{
|
|
|
this.XA70080(sn, ieee);
|
|
|
}
|
|
|
else if (deviceId == 0x0051)
|
|
|
{
|
|
|
this.X8D07020000(sn, ieee);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x95删除指定设备
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="ieee"></param>
|
|
|
public void X95(string sn, string ieee)
|
|
|
{
|
|
|
this._logger.LogDebug($"delete {ieee} from {sn}");
|
|
|
var list = new List<byte>();
|
|
|
list.AddRange(ieee.HexToBytes());
|
|
|
list.Add(0x01);
|
|
|
this.Write(sn, RequestType.x95, ieee, list, 0);
|
|
|
}
|
|
|
|
|
|
//0x94更改指定设备名
|
|
|
/// <summary>
|
|
|
/// 0x82设置指定设备的开关状态
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="ieee"></param>
|
|
|
/// <param name="status"></param>
|
|
|
public void X82(string sn, string ieee, byte status, byte? ep = null)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
status
|
|
|
};
|
|
|
this.Write(sn, RequestType.x82, ieee, list, ep: ep);
|
|
|
}
|
|
|
|
|
|
public void X82OpenDoor(string sn, string ieee, byte status = 1, byte? ep = null)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
status,0x06,0x01,0x02,0x03,0x04,0x05,0x06
|
|
|
};
|
|
|
this.Write(sn, RequestType.x82, ieee, list, ep: ep);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x85-0x07接收指定设备的开关状态
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X07(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
|
|
|
if (device != null)
|
|
|
{
|
|
|
Data deviceData = null;
|
|
|
var switchState = ms.ReadInt();
|
|
|
if (device.Name.Contains("二路开关") || device.Name.Contains("三路开关"))
|
|
|
{
|
|
|
if (endpoint == 0x10)
|
|
|
{
|
|
|
deviceData = device.CreateData(Keys.L1State, switchState == 0 ? "关" : "开", DeviceDataType.String, "L1状态", timestamp: timestamp);
|
|
|
}
|
|
|
else if (endpoint == 0x11)
|
|
|
{
|
|
|
deviceData = device.CreateData(Keys.L2State, switchState == 0 ? "关" : "开", DeviceDataType.String, "L2状态", timestamp: timestamp);
|
|
|
}
|
|
|
else if (endpoint == 0x12)
|
|
|
{
|
|
|
deviceData = device.CreateData(Keys.L3State, switchState == 0 ? "关" : "开", DeviceDataType.String, "L3状态", timestamp: timestamp);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
deviceData = device.CreateData(Keys.State, switchState == 0 ? "关" : (switchState == 1 ? "开" : "停"), DeviceDataType.String, "状态", timestamp: timestamp);
|
|
|
}
|
|
|
if (deviceData != null)
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, deviceData);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"device {address} hasn't save in database");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x83设置指定设备的亮度
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="ieee"></param>
|
|
|
/// <param name="value">0-255</param>
|
|
|
public void X83(string sn, string ieee, [Range(0, 255)] byte brightness)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
brightness
|
|
|
};
|
|
|
list.AddRange(new byte[] { 0x00, 0x00 });
|
|
|
this.Write(sn, RequestType.x83, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x08接收指定设备的亮度状态返回值
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X08(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
|
|
|
if (device != null)
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Brightness, ms.ReadByte(), DeviceDataType.Int, "亮度", timestamp: timestamp));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"device {address} hasn't save in database");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x84设置指定设备的颜色,即色调和饱和度
|
|
|
/// </summary>
|
|
|
public void X84(string sn, string ieee, [Range(0, 255)] byte hue, [Range(0, 255)] byte saturation)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
hue,
|
|
|
saturation
|
|
|
};
|
|
|
list.AddRange(new byte[] { 0x00, 0x00 });
|
|
|
this.Write(sn, RequestType.x84, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x09接收指定设备的色调返回值
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X09(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
|
|
|
if (device != null)
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Hue, ms.ReadByte(), DeviceDataType.Int, "色调", timestamp: timestamp));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"device {address} hasn't save in database");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x09接收指定设备的饱和度返回值
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X0A(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
|
|
|
if (device != null)
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Saturation, ms.ReadByte(), DeviceDataType.Int, "饱和度", timestamp: timestamp));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"device {address} hasn't save in database");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x85-0x07查询开关状态
|
|
|
/// </summary>
|
|
|
public void X85(string sn, string ieee)
|
|
|
{
|
|
|
var list = new List<byte>();
|
|
|
this.Write(sn, RequestType.x85, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x86-0x08查询亮度状态
|
|
|
/// </summary>
|
|
|
public void X86(string sn, string ieee)
|
|
|
{
|
|
|
var list = new List<byte>();
|
|
|
this.Write(sn, RequestType.x86, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x87-0x09查询色度状态
|
|
|
/// </summary>
|
|
|
public void X87(string sn, string ieee)
|
|
|
{
|
|
|
var list = new List<byte>();
|
|
|
this.Write(sn, RequestType.x87, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x88-0x0a查询饱和度状态
|
|
|
/// </summary>
|
|
|
public void X88(string sn, string ieee)
|
|
|
{
|
|
|
var list = new List<byte>();
|
|
|
this.Write(sn, RequestType.x88, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x8d-0x70发送zcl指令,读取簇id为0702属性为0000的电量值
|
|
|
/// </summary>
|
|
|
public void X8D07020000(string sn, string ieee)
|
|
|
{
|
|
|
this.Write(sn, RequestType.xa7, ieee, new List<byte>() { 0x02, 0x07, 0x00, 0x00, 0x00 }, 2);
|
|
|
this.Write(sn, RequestType.xa7, ieee, new List<byte>() { 0x02, 0x07, 0x0b, 0x05, 0x00 }, 2);
|
|
|
}
|
|
|
|
|
|
//
|
|
|
//0x96
|
|
|
//0x8e
|
|
|
//0x8f
|
|
|
//0x97
|
|
|
//0x98
|
|
|
//0x90
|
|
|
//0x91
|
|
|
//0x92
|
|
|
//0x93
|
|
|
//0x8a
|
|
|
//0x8b
|
|
|
//0x8c
|
|
|
//0x99
|
|
|
//0x9a
|
|
|
//0x9b
|
|
|
//0x9c
|
|
|
//0x9d获取网关信息
|
|
|
public void X9d(string sn)
|
|
|
{
|
|
|
this.Write(sn, RequestType.x9d);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x15接收网关信息
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X15(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var version = ms.ReadASIIString(5);
|
|
|
var snid = ms.ReadHexString(3);
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = deviceRepo.Table()
|
|
|
.Include(o => o.Data)
|
|
|
.FirstOrDefault(o => o.Number == sn);
|
|
|
if (device == null)
|
|
|
{
|
|
|
this._logger.LogDebug($"gateway {sn} hasn't save in database");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Version, version, DeviceDataType.String, "版本", timestamp: timestamp));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.DeviceCount, ms.ReadByte(), DeviceDataType.Int, "设备数量", timestamp: timestamp));
|
|
|
var userName = ms.ReadASIIString(20);
|
|
|
var password = ms.ReadASIIString(20);
|
|
|
device.UserName = userName;
|
|
|
device.Password = password;
|
|
|
deviceRepo.SaveChanges();
|
|
|
ms.ReadByte();
|
|
|
ms.ReadByte();
|
|
|
ms.ReadByte();
|
|
|
ms.ReadByte();
|
|
|
var hex = ms.ReadHexString(5);
|
|
|
var compileVersion = ms.ReadHexString(5);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//0x9e
|
|
|
//0x9f
|
|
|
//0xa0
|
|
|
//0xa1
|
|
|
//0xa2
|
|
|
//0xa3
|
|
|
//0xa4
|
|
|
//0xa5
|
|
|
/// <summary>
|
|
|
/// 0xa8设置指定设备的色温
|
|
|
/// </summary>
|
|
|
public void XA8(string sn, string ieee, [Range(2700, 6500)] ushort color)
|
|
|
{
|
|
|
var list = new List<byte>();
|
|
|
list.AddRange(BitConverter.GetBytes(color));
|
|
|
list.AddRange(new byte[] { 0x00, 0x00 });
|
|
|
this.Write(sn, RequestType.xa8, ieee, list);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0x27接收指定设备的色温返回值
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X27(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
|
|
|
if (device != null)
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.ColorTemperature, ms.ReadInt(), DeviceDataType.Int, "色温", timestamp: timestamp));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogDebug($"device {address} hasn't save in database");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0080查询红外版本号
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="ieee"></param>
|
|
|
public void XA70080(string sn, string ieee)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
0x03,//控制标志
|
|
|
0xff,//包长
|
|
|
0x00//包序号
|
|
|
};
|
|
|
list.AddRange(new byte[] { 0x55, 0x55 });
|
|
|
list.Add(0x02);
|
|
|
list.AddRange(new byte[] { 0x80, 0x00 });
|
|
|
list.Add(0x82);
|
|
|
list[1] = (byte)(list.Count() - 3);
|
|
|
this.Write(sn, RequestType.xa7, ieee, list, 2);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0081开始匹配遥控器
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="ieee"></param>
|
|
|
public void XA70081(string sn, string ieee, string version, [Range(1, 5)] byte type)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
0x03,//控制标志
|
|
|
0xff,//包长
|
|
|
0x00//包序号
|
|
|
};
|
|
|
list.AddRange(new byte[] { 0x55, 0x55 });
|
|
|
list.Add(0x09);
|
|
|
list.AddRange(version.HexToBytes());
|
|
|
list.AddRange(new byte[] { 0x81, 0x00 });
|
|
|
list.Add(type);
|
|
|
list[1] = (byte)(list.Count() - 3 + 2);
|
|
|
list.Add((byte)list.Skip(list.Count - 10).Take(10).Select(o => (int)o).Sum());
|
|
|
//list.Add(0x1a);
|
|
|
this.Write(sn, RequestType.xa7, ieee, list, 2);
|
|
|
}
|
|
|
|
|
|
public void XA70082(string sn, string ieee, byte type, ushort code)
|
|
|
{
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
0x03,//控制标志
|
|
|
0xff,//包长
|
|
|
0x00//包序号
|
|
|
};
|
|
|
list.AddRange(new byte[] { 0x55, 0x55 });
|
|
|
list.Add(0x0b);
|
|
|
using (var scope = _applicationServices.CreateScope())
|
|
|
{
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var number = $"{sn}-{ieee}";
|
|
|
var device = deviceRepo.Table().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
|
|
|
var version = device.GetDataValue(Keys.Version);
|
|
|
list.AddRange(version.HexToBytes());
|
|
|
}
|
|
|
list.AddRange(new byte[] { 0x82, 0x00 });
|
|
|
list.Add(type);
|
|
|
list.AddRange(BitConverter.GetBytes(code));
|
|
|
list[1] = (byte)(list.Count() - 3 + 1);
|
|
|
list.Add((byte)list.Skip(5).Take(12).Select(o => (int)o).Sum());
|
|
|
this.Write(sn, RequestType.xa7, ieee, list, 2);
|
|
|
}
|
|
|
|
|
|
public void XA70083(string sn, string ieee, byte type, ushort code)
|
|
|
{
|
|
|
if (type == 1)
|
|
|
{
|
|
|
if (code <= 602)
|
|
|
{
|
|
|
throw new Exception($"code {code} must > 602 when type is {type} ");
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (code <= 43)
|
|
|
{
|
|
|
throw new Exception($"code {code} must > 43 when type is {type} ");
|
|
|
}
|
|
|
}
|
|
|
var list = new List<byte>
|
|
|
{
|
|
|
0x03,//控制标志
|
|
|
0xff,//包长
|
|
|
0x00//包序号
|
|
|
};
|
|
|
list.AddRange(new byte[] { 0x55, 0x55 });
|
|
|
list.Add(0x0b);
|
|
|
using (var scope = _applicationServices.CreateScope())
|
|
|
{
|
|
|
var repo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var number = $"{sn}-{ieee}";
|
|
|
var device = repo.ReadOnlyTable().Include(o => o.Data).FirstOrDefault(o => o.Number == number);
|
|
|
var version = device.GetDataValue(Keys.Version);
|
|
|
list.AddRange(version.HexToBytes());
|
|
|
}
|
|
|
list.AddRange(new byte[] { 0x83, 0x00 });
|
|
|
list.Add(type);
|
|
|
list.AddRange(BitConverter.GetBytes(code));
|
|
|
list[1] = (byte)(list.Count() - 3 + 2);
|
|
|
list.Add((byte)list.Skip(5).Take(12).Select(o => (int)o).Sum());
|
|
|
//list.Add(0x1a);
|
|
|
this.Write(sn, RequestType.xa7, ieee, list, 2);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0xa9-0x27查询色温状态
|
|
|
/// </summary>
|
|
|
public void XA9(string sn, string ieee)
|
|
|
{
|
|
|
var list = new List<byte>();
|
|
|
this.Write(sn, RequestType.xa9, ieee, list);
|
|
|
}
|
|
|
|
|
|
//0xac
|
|
|
//0xaf
|
|
|
//0xb0
|
|
|
/// <summary>
|
|
|
/// 0x70设备上报信息
|
|
|
/// </summary>
|
|
|
/// <param name="sn"></param>
|
|
|
/// <param name="data"></param>
|
|
|
private void X70(string sn, byte[] data)
|
|
|
{
|
|
|
var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
using var ms = new MemoryStream(data);
|
|
|
var responseType = ms.ReadByte();
|
|
|
var dataLength = ms.ReadByte();
|
|
|
var address = ms.ReadHexStringDesc(2);
|
|
|
var endpoint = ms.ReadByte();
|
|
|
using var scope = _applicationServices.CreateScope();
|
|
|
var deviceRepo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
var device = this.GetDeviceByAddress(deviceRepo, sn, address);
|
|
|
var clusterId = (ClusterId)ms.ReadInt();
|
|
|
if (device != null)
|
|
|
{
|
|
|
var deviceId = Convert.ToInt32(device.GetDataValue(Keys.DeviceId));
|
|
|
var reportCount = ms.ReadByte();
|
|
|
var props = new Dictionary<int, byte[]>();
|
|
|
for (int i = 0; i < reportCount; i++)
|
|
|
{
|
|
|
var propId = ms.ReadInt();
|
|
|
var propDataTypeValue = ms.ReadByte();
|
|
|
int propDataLength;
|
|
|
if (Enum.IsDefined(typeof(FBeeDataType), propDataTypeValue))
|
|
|
{
|
|
|
var propDataType = (FBeeDataType)propDataTypeValue;
|
|
|
if (propDataType == FBeeDataType.bitstring || propDataType == FBeeDataType.characterstring)
|
|
|
{
|
|
|
propDataLength = ms.ReadByte();
|
|
|
}
|
|
|
else if (propDataType == FBeeDataType.longbitstring || propDataType == FBeeDataType.longcharacterstring)
|
|
|
{
|
|
|
propDataLength = ms.ReadInt();
|
|
|
}
|
|
|
else if (propDataType == FBeeDataType.sequence || propDataType == FBeeDataType.set || propDataType == FBeeDataType.bag)
|
|
|
{
|
|
|
propDataLength = ms.ReadInt();
|
|
|
}
|
|
|
else if (propDataType == FBeeDataType.unknown)
|
|
|
{
|
|
|
propDataLength = 0;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
propDataLength = Convert.ToInt32(Regex.Match(propDataType.GetName(), @"(\d+)$").Groups[1].Value);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
this._logger.LogError(ex.ToString());
|
|
|
propDataLength = 1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
propDataLength = 1;
|
|
|
}
|
|
|
var propData = ms.Read(propDataLength);
|
|
|
props.Add(propId, propData);
|
|
|
}
|
|
|
if (clusterId == ClusterId.off)
|
|
|
{
|
|
|
var status = props.First().Value[0];
|
|
|
if (status == 0x00)//离网
|
|
|
{
|
|
|
DeleteDevice(deviceRepo, device);
|
|
|
}
|
|
|
else if (status == 0x03)//入网
|
|
|
{
|
|
|
}
|
|
|
}
|
|
|
else if (clusterId == ClusterId.light)
|
|
|
{
|
|
|
var light = BitConverter.ToInt16(props[0x0000]);
|
|
|
var desc = this.GetDescription(Keys.Light, light);
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Light, light, DeviceDataType.Int, "光照度", "lux", desc, timestamp: timestamp));
|
|
|
}
|
|
|
else if (clusterId == ClusterId.alarm)
|
|
|
{
|
|
|
var state = BitConverter.ToInt16(props[0x0080]);
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Warning, state == 1 ? "警报" : "正常", DeviceDataType.String, "状态", timestamp: timestamp));
|
|
|
|
|
|
//var data2 = device.CreateData(Keys.UnderVoltage, new BitArray(props[0x0080])[3], DeviceDataType.Int, "低电量");
|
|
|
//device.AddorUpdateData(data2);
|
|
|
//var dto2 = data2.To<EditDataModel>();
|
|
|
//dto2.DeviceNumber = device.Number;
|
|
|
//dataList.Add(dto2);
|
|
|
}
|
|
|
else if (clusterId == ClusterId.pm25)
|
|
|
{
|
|
|
var pm2d5 = BitConverter.ToInt16(props[0x0000]);
|
|
|
var pm1d0 = BitConverter.ToInt16(props[0x0002]);
|
|
|
var pm10 = BitConverter.ToInt16(props[0x0001]);
|
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.PM25, pm2d5, DeviceDataType.Int, "PM2.5", "μg/m³", this.GetDescription(Keys.PM25, pm2d5), timestamp: timestamp));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.PM100, pm1d0, DeviceDataType.Int, "PM10", "μg/m³", this.GetDescription(Keys.PM100, pm1d0), timestamp: timestamp));
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.PM10, pm10, DeviceDataType.Int, "PM1.0", "μg/m³"));
|
|
|
}
|
|
|
else if (clusterId == ClusterId.temperature)
|
|
|
{
|
|
|
var temperature = BitConverter.ToInt16(props[0x0000]) / 100f;
|
|
|
var desc = this.GetDescription(Keys.Temperature, temperature);
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Temperature, temperature, DeviceDataType.Float, "温度", "℃", desc, timestamp: timestamp));
|
|
|
}
|
|
|
else if (clusterId == ClusterId.humidity)
|
|
|
{
|
|
|
var humidity = BitConverter.ToInt16(props[0x0000]) / 100f;
|
|
|
var desc = this.GetDescription(Keys.Humidity, humidity);
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Humidity, humidity, DeviceDataType.Float, "湿度", "RH%", desc, timestamp: timestamp));
|
|
|
}
|
|
|
else if (clusterId == ClusterId.voltage)
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Voltage, props[0x21][0] / 2f, DeviceDataType.Float, "电量", hidden: true, timestamp: timestamp));
|
|
|
}
|
|
|
else if (clusterId == ClusterId.socket)
|
|
|
{
|
|
|
if (props.Keys.Contains(0x0000))
|
|
|
{
|
|
|
var tempBytes = new List<byte>();
|
|
|
tempBytes.AddRange(props[0x00]);
|
|
|
tempBytes.Add(0x00);
|
|
|
tempBytes.Add(0x00);
|
|
|
var electricity = BitConverter.ToInt64(tempBytes.ToArray()) / 1000f;
|
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Electricity, electricity, DeviceDataType.Float, "电量", "kWh", timestamp: timestamp));
|
|
|
}
|
|
|
}
|
|
|
else if (clusterId == ClusterId.doorlock)
|
|
|
{
|
|
|
}
|
|
|
if (deviceId == 0x0163)
|
|
|
{
|
|
|
if (props.ContainsKey(0x400a))
|
|
|
{
|
|
|
var irdata = props[0x400a];
|
|
|
if (irdata.Length == 10)
|
|
|
{
|
|
|
var irVersion = BitConverter.ToString((props[0x400a].Skip(3).Take(6).ToArray())).Replace("-", "").ToLower();
|
|
|
if (!string.IsNullOrEmpty(irVersion))
|
|
|
{
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Version, irVersion, DeviceDataType.String, "红外版本", timestamp: timestamp));
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
var irtype = BitConverter.ToInt16(irdata.Skip(9).Take(2).ToArray());
|
|
|
if (irtype == 0x0082)
|
|
|
{
|
|
|
var irDeviceType = irdata.Skip(11).First();
|
|
|
if (irDeviceType == 0x01)
|
|
|
{
|
|
|
var keyCode = BitConverter.ToInt16(irdata.Skip(12).Take(2).ToArray());
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.KeyPress, keyCode, DeviceDataType.Int, "按键", timestamp: timestamp));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else if (deviceId == 0x0051)
|
|
|
{
|
|
|
foreach (var item in props)
|
|
|
{
|
|
|
if (item.Key == 0x0000)
|
|
|
{
|
|
|
var tempBytes = new List<byte>();
|
|
|
tempBytes.AddRange(item.Value);
|
|
|
tempBytes.Add(0x00);
|
|
|
tempBytes.Add(0x00);
|
|
|
if (tempBytes.Count == 8)
|
|
|
{
|
|
|
var electricity = BitConverter.ToInt64(tempBytes.ToArray()) / 10000f;
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Electricity, electricity.ToString("f2"), DeviceDataType.Float, "电量", "kWh", timestamp: timestamp));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this._logger.LogError($"data length must be 8 but now is {tempBytes.Count}");
|
|
|
}
|
|
|
}
|
|
|
else if (item.Key == 0x050b)
|
|
|
{
|
|
|
var power = BitConverter.ToUInt16(item.Value.ToArray());
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData(Keys.Power, power, DeviceDataType.Float, "功率", "W", timestamp: timestamp));
|
|
|
}
|
|
|
}
|
|
|
if (clusterId == ClusterId.SummationDivisor)
|
|
|
{
|
|
|
this.X8D07020000(sn, device.Number);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static void DeleteDevice(IRepository<Device> deviceRepo, Device device)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var model = device.To<EditDeviceModel>();
|
|
|
deviceRepo.Delete(device);
|
|
|
deviceRepo.SaveChanges();
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
ex.PrintStack();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private string GetDescription(string key, short value)
|
|
|
{
|
|
|
if (key == Keys.Light)
|
|
|
{
|
|
|
return value < 300 ? "低" : (value > 500 ? "高" : "适中");
|
|
|
}
|
|
|
else if (key == Keys.PM25)
|
|
|
{
|
|
|
return value <= 35 ? "优" : (value < 75 ? "良" : "污染");
|
|
|
}
|
|
|
else if (key == Keys.PM100)
|
|
|
{
|
|
|
return value <= 50 ? "优" : (value < 75 ? "100" : "污染");
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
private string GetDescription(string key, float value)
|
|
|
{
|
|
|
if (key == Keys.Temperature)
|
|
|
{
|
|
|
return value < 16 ? "低" : (value > 28 ? "高" : "适中");
|
|
|
}
|
|
|
else if (key == Keys.Humidity)
|
|
|
{
|
|
|
return value < 30 ? "低" : (value > 80 ? "高" : "适中");
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
//0xb1
|
|
|
//0xb2
|
|
|
//0xc2
|
|
|
//0xc3
|
|
|
//0xc4
|
|
|
//0xc5
|
|
|
//0x75
|
|
|
|
|
|
#endregion api
|
|
|
|
|
|
private Device GetDevice(IRepository<Device> deviceRepo, string sn, string ieee)
|
|
|
{
|
|
|
var number = $"{sn}-{ieee}";
|
|
|
return deviceRepo.Table()
|
|
|
.Include(o => o.Data)
|
|
|
.FirstOrDefault(o => o.Number == number);
|
|
|
}
|
|
|
|
|
|
private Device GetDeviceByAddress(IRepository<Device> deviceRepo, string sn, string address)
|
|
|
{
|
|
|
return deviceRepo.Table()
|
|
|
.Include(o => o.Data)
|
|
|
.FirstOrDefault(o => o.Number.StartsWith(sn) && o.Data.Any(d => d.Key == Keys.Address && d.Value == address));
|
|
|
}
|
|
|
}
|
|
|
} |