From bac245a138c9b34e9902db5455e84d6c2a0be20b Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Wed, 15 Apr 2020 17:39:04 +0200 Subject: [PATCH] MC Add Zigbee2Mqtt device handle (+ Aqara Device, Aqara Cube, Aqara Switch) + arlo API postman collection + network monitor script --- MyCore/Arlo API.postman_collection.json | 210 ++++++++++++++++++ .../Zigbee/Aqara/{Cube.cs => AqaraCube.cs} | 6 +- .../Providers/Zigbee/Aqara/AqaraDevice.cs | 14 ++ .../Providers/Zigbee/Aqara/AqaraSwitch.cs | 22 ++ .../Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs | 26 +++ MyCore/Services/Devices/DeviceService.cs | 1 + MyCore/Services/Devices/MQTTService.cs | 59 ++++- .../SupportedDevices/YeelightService.cs | 1 - MyCore/Startup.cs | 8 +- RPI Code/network-monitor.sh | 43 ++++ 10 files changed, 380 insertions(+), 10 deletions(-) create mode 100644 MyCore/Arlo API.postman_collection.json rename MyCore/Models/Providers/Zigbee/Aqara/{Cube.cs => AqaraCube.cs} (69%) create mode 100644 MyCore/Models/Providers/Zigbee/Aqara/AqaraDevice.cs create mode 100644 MyCore/Models/Providers/Zigbee/Aqara/AqaraSwitch.cs create mode 100644 MyCore/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs create mode 100644 RPI Code/network-monitor.sh diff --git a/MyCore/Arlo API.postman_collection.json b/MyCore/Arlo API.postman_collection.json new file mode 100644 index 0000000..0f51be6 --- /dev/null +++ b/MyCore/Arlo API.postman_collection.json @@ -0,0 +1,210 @@ +{ + "variables": [], + "info": { + "name": "Arlo API", + "_postman_id": "665ada25-ab68-51d0-aa6e-ab376ded9d8a", + "description": "", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" + }, + "item": [ + { + "name": "Login Arlo", + "request": { + "url": "https://arlo.netgear.com/hmsweb/login/v2", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\"email\":\"fransolet.thomas@gmail.com\",\"password\":\"Coconuts09\"}" + }, + "description": "" + }, + "response": [] + }, + { + "name": "User Profile", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/profile", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "User Session", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/session", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "User Friends", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/friends", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "User Location", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/locations", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "User Service Level", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/serviceLevel/v2", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "Get Devices", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/devices", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "Get Library", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/library", + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + }, + { + "key": "Content-Type", + "value": "application/json;charset=UTF-8", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\"dateFrom\": \"20190126\",\"dateTo\": \"20200105\"}" + }, + "description": "" + }, + "response": [] + }, + { + "name": "Get Library Metadata", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/library/metadata/v2", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + }, + { + "key": "", + "value": "", + "description": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\"dateFrom\": \"20190126\",\"dateTo\": \"20200105\"}" + }, + "description": "" + }, + "response": [] + }, + { + "name": "Get Payment offers", + "request": { + "url": "https://arlo.netgear.com/hmsweb/users/payment/offers", + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "2_5MS700UkeZA_mvcLkTG2MEmHR-mEKNG2M4MmPIaYKMmJa2c-TlCs4kTaCr0i6gXha10UY7HVl_RBmG71Ub4_wYSyT2kawx1KKMkCsSxp_9E4H0oLNsNrMUhgfB2n6pem6xS39Y5fHCc1gT_IpzBfP7MbQPRgrVeh177DHdvUd47Z", + "description": "" + }, + { + "key": "", + "value": "", + "description": "", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\"dateFrom\": \"20190126\",\"dateTo\": \"20200105\"}" + }, + "description": "" + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/MyCore/Models/Providers/Zigbee/Aqara/Cube.cs b/MyCore/Models/Providers/Zigbee/Aqara/AqaraCube.cs similarity index 69% rename from MyCore/Models/Providers/Zigbee/Aqara/Cube.cs rename to MyCore/Models/Providers/Zigbee/Aqara/AqaraCube.cs index 93671b6..7e21e7e 100644 --- a/MyCore/Models/Providers/Zigbee/Aqara/Cube.cs +++ b/MyCore/Models/Providers/Zigbee/Aqara/AqaraCube.cs @@ -1,5 +1,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using MyCore.Models.Providers.Zigbee.Aqara; using System; using System.Collections.Generic; using System.Linq; @@ -7,13 +8,10 @@ using System.Threading.Tasks; namespace MyCore.Models.Aqara { - public class Cube + public class AqaraCube : AqaraDevice { public double Angle { get; set; } - public int LinkQuality { get; set; } public int Side { get; set; } - public int Battery { get; set; } - public int Voltage { get; set; } public string Action { get; set; } } } \ No newline at end of file diff --git a/MyCore/Models/Providers/Zigbee/Aqara/AqaraDevice.cs b/MyCore/Models/Providers/Zigbee/Aqara/AqaraDevice.cs new file mode 100644 index 0000000..f2212f1 --- /dev/null +++ b/MyCore/Models/Providers/Zigbee/Aqara/AqaraDevice.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MyCore.Models.Providers.Zigbee.Aqara +{ + public class AqaraDevice + { + public int LinkQuality { get; set; } + public int Battery { get; set; } + public int Voltage { get; set; } + } +} diff --git a/MyCore/Models/Providers/Zigbee/Aqara/AqaraSwitch.cs b/MyCore/Models/Providers/Zigbee/Aqara/AqaraSwitch.cs new file mode 100644 index 0000000..6f4bf13 --- /dev/null +++ b/MyCore/Models/Providers/Zigbee/Aqara/AqaraSwitch.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MyCore.Models.Providers.Zigbee.Aqara +{ + public class AqaraSwitch : AqaraDevice + { + public string Click { get; set; } + public int Duration { get; set; } + } + + /*public enum ClickOptions { + single, + double, + triple, + quadruple, + long, + long_release + }*/ +} diff --git a/MyCore/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs b/MyCore/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs new file mode 100644 index 0000000..4f26c7d --- /dev/null +++ b/MyCore/Models/Providers/Zigbee/Zigbee2Mqtt/Zigbee2MqttDevice.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MyCore.Models.Providers.Zigbee.Zigbee2Mqtt +{ + public class Zigbee2MqttDevice + { + public string ieeeAddr { get; set; } + public string type { get; set; } + public int networkAddress { get; set; } + public string model { get; set; } + public string vendor { get; set; } + public string description { get; set; } + public string friendly_name { get; set; } + public int manufacturerID { get; set; } + public string manufacturerName { get; set; } + public string powerSource { get; set; } + public string modelID { get; set; } + public int hardwareVersion { get; set; } + public string softwareBuildID { get; set; } + public string dateCode { get; set; } + public long lastSeen { get; set; } + } +} \ No newline at end of file diff --git a/MyCore/Services/Devices/DeviceService.cs b/MyCore/Services/Devices/DeviceService.cs index ea0db52..77c5b0e 100644 --- a/MyCore/Services/Devices/DeviceService.cs +++ b/MyCore/Services/Devices/DeviceService.cs @@ -121,6 +121,7 @@ namespace MyCore.Services.Devices // TODO Server.. MQTTService mQTTService = new MQTTService("192.168.31.140", "mqtt", "mqtt"); + mQTTService.GetDevices(); } catch (UnauthorizedAccessException ex) { diff --git a/MyCore/Services/Devices/MQTTService.cs b/MyCore/Services/Devices/MQTTService.cs index 0d625ae..2534d3e 100644 --- a/MyCore/Services/Devices/MQTTService.cs +++ b/MyCore/Services/Devices/MQTTService.cs @@ -4,6 +4,8 @@ using MQTTnet.Client.Options; using MyCore.Models; using MyCore.Models.Aqara; using MyCore.Models.Ikea; +using MyCore.Models.Providers.Zigbee.Aqara; +using MyCore.Models.Providers.Zigbee.Zigbee2Mqtt; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -25,6 +27,8 @@ namespace MyCore.Services private LightState lightStateIkeaBulb = LightState.Undefined; + public List devices = new List(); + // It's here to have the mqtt initialisation + logic for payload.. // Related to which event occurs, a specific action is done. @@ -53,6 +57,7 @@ namespace MyCore.Services if (res.Status == TaskStatus.RanToCompletion) { Console.WriteLine("It's connected"); + PublishMessage("zigbee2mqtt/bridge/config/devices/get", ""); } else { @@ -90,13 +95,19 @@ namespace MyCore.Services { Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}"); - Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); + var payload = ""; + if (e.ApplicationMessage.Payload != null) + { + Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); + payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + } + + Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); Console.WriteLine(); var topic = e.ApplicationMessage.Topic; - var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); // As soon as we received all the info => //_client.DisconnectAsync(); @@ -120,10 +131,21 @@ namespace MyCore.Services // TODO - TO CLARIFY switch (topic) { + case "zigbee2mqtt/bridge/config/devices": + try + { + var test = JsonConvert.DeserializeObject>(payload); + devices = test; + } + catch (Exception ex) + { + Console.WriteLine($"Error during retrieving devices ! Exception: {ex}"); + } + break; case "zigbee2mqtt/0x00158d00029a7b65": try { - var test = JsonConvert.DeserializeObject(payload); + var test = JsonConvert.DeserializeObject(payload); if (test.Action == "shake") { var labLamp = yeelightService.devices.Where(d => d.Hostname == "192.168.31.74").FirstOrDefault(); @@ -157,6 +179,32 @@ namespace MyCore.Services Console.WriteLine($"Error IkeaLightBulb ! Exception: {ex}"); } break; + case "zigbee2mqtt/0x00158d00035cf1a7": + try + { + var aqaraSwitch = JsonConvert.DeserializeObject(payload); + + if (aqaraSwitch.Click == "single") { + YeelightService yeelighService = new YeelightService(); + var devicesYeelight = yeelighService.GetDevices().Result; + var labLamp = devicesYeelight.Where(light => light.Hostname == "192.168.31.74").FirstOrDefault(); + if (labLamp != null) + yeelightService.Toggle(labLamp); + } + + if (aqaraSwitch.Click == "double") + { + if (lightStateIkeaBulb == LightState.Undefined || lightStateIkeaBulb == LightState.Off) + PublishMessage("zigbee2mqtt/0x14b457fffe7628fa/set", "{\"state\": \"ON\"}"); + else + PublishMessage("zigbee2mqtt/0x14b457fffe7628fa/set", "{\"state\": \"OFF\"}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error Aqara switch ! Exception: {ex}"); + } + break; default: Console.WriteLine("Hello nothing to do here.."); break; @@ -185,6 +233,11 @@ namespace MyCore.Services await _client.PublishAsync(mqttMessage); } + public List GetDevices() + { + return devices; + } + /*protected async Task Start() { var server = new MqttFactory().CreateMqttServer(); diff --git a/MyCore/Services/Devices/SupportedDevices/YeelightService.cs b/MyCore/Services/Devices/SupportedDevices/YeelightService.cs index ff5b8e9..2318522 100644 --- a/MyCore/Services/Devices/SupportedDevices/YeelightService.cs +++ b/MyCore/Services/Devices/SupportedDevices/YeelightService.cs @@ -14,7 +14,6 @@ namespace MyCore.Services { devices = await DeviceLocator.Discover(); - //Toggle(devices[1]); return devices; } diff --git a/MyCore/Startup.cs b/MyCore/Startup.cs index 78b3acd..b2bc2eb 100644 --- a/MyCore/Startup.cs +++ b/MyCore/Startup.cs @@ -33,8 +33,12 @@ namespace MyCore //ArloService arloService = new ArloService(); - YeelightService yeelighService = new YeelightService(); - yeelighService.GetDevices(); + /*YeelightService yeelighService = new YeelightService(); + yeelighService.GetDevices();*/ + + MQTTService mQTTService = new MQTTService("192.168.31.140", "mqtt", "mqtt"); + + mQTTService.GetDevices(); } diff --git a/RPI Code/network-monitor.sh b/RPI Code/network-monitor.sh new file mode 100644 index 0000000..14e4906 --- /dev/null +++ b/RPI Code/network-monitor.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# make sure we aren't running already +what=`basename $0` +for p in `ps h -o pid -C $what`; do + if [ $p != $$ ]; then + exit 0 + fi +done + +# source configuration +. /etc/wifi.conf + +exec 1> /dev/null +exec 2>> $log +echo $(date) > $log +# without check_interval set, we risk a 0 sleep = busy loop +if [ ! "$check_interval" ]; then + echo "No check interval set!" >> $log + exit 1 +fi + +startWifi () { + dhclient -v -r + # make really sure + killall dhclient + iwconfig $wlan essid $essid + dhclient -v $wlan +} + +ifconfig $eth down +ifconfig $wlan up +startWifi + +while [ 1 ]; do + ping -c 1 $router_ip & wait $! + if [ $? != 0 ]; then + echo $(date)" attempting restart..." >> $log + startWifi + sleep 1 + else sleep $check_interval + fi +done \ No newline at end of file