Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MClimate: add devices - 16ADS, Button, Open/close; add profileIds #867

Merged
merged 10 commits into from
Jan 28, 2025
11 changes: 11 additions & 0 deletions vendor/mclimate/16ads-codec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
uplinkDecoder:
fileName: 16ads.js
examples:
- description: Periodic uplink
input:
fPort: 2
bytes: [0x01, 0x1C, 0x01]
output:
data:
internalTemperature: 28
relayState: 'ON'
8 changes: 8 additions & 0 deletions vendor/mclimate/16ads-profile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
supportsClassB: false
supportsClassC: true
macVersion: 1.0.3
regionalParametersVersion: RP001-1.0.3-RevA
supportsJoin: true
maxEIRP: 16
supports32bitFCnt: true
classCTimeout: 60
142 changes: 142 additions & 0 deletions vendor/mclimate/16ads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const decodeUplink = (input) => {
try {
let { bytes } = input;
let data = {};

const handleKeepalive = (bytes, data) => {
// Temperature sign and internal temperature
const isNegative = (bytes[1] & 0x80) !== 0; // Check the 7th bit for the sign

let temperature = bytes[1] & 0x7F; // Mask out the 7th bit to get the temperature value
data.internalTemperature = isNegative ? -temperature : temperature;

// Relay state
data.relayState = bytes[2] === 0x01 ? "ON" : "OFF";

return data;
};

const handleResponse = (bytes, data) => {
let commands = bytes.map(byte => (`0${byte.toString(16)}`).slice(-2)).slice(0, -3);
let command_len = 0;

commands.forEach((command, i) => {
switch (command) {
case '04': {
command_len = 2;
const hardwareVersion = commands[i + 1];
const softwareVersion = commands[i + 2];
data.deviceVersions = {
hardware: Number(hardwareVersion),
software: Number(softwareVersion),
};
break;
}
case '12': {
command_len = 1;
data.keepAliveTime = parseInt(commands[i + 1], 16);
break;
}
case '19': {
command_len = 1;
const commandResponse = parseInt(commands[i + 1], 16);
const periodInMinutes = (commandResponse * 5) / 60;
data.joinRetryPeriod = periodInMinutes;
break;
}
case '1b': {
command_len = 1;
data.uplinkType = parseInt(commands[i + 1], 16);
break;
}
case '1d': {
command_len = 2;
const wdpC = commands[i + 1] === '00' ? false : parseInt(commands[i + 1], 16);
const wdpUc = commands[i + 2] === '00' ? false : parseInt(commands[i + 2], 16);
data.watchDogParams = { wdpC, wdpUc };
break;
}
case '1f': {
command_len = 2;
data.overheatingThresholds = {
trigger: parseInt(commands[i + 1], 16),
recovery: parseInt(commands[i + 2], 16),
};
break;
}
case '5a': {
command_len = 1;
data.afterOverheatingProtectionRecovery = parseInt(commands[i + 1], 16);
break;
}
case '5c': {
command_len = 1;
data.ledIndicationMode = parseInt(commands[i + 1], 16);
break;
}
case '5d': {
command_len = 1;
data.manualChangeRelayState = parseInt(commands[i + 1], 16) === 0x01;
break;
}
case '5f': {
command_len = 1;
data.relayRecoveryState = parseInt(commands[i + 1], 16);
break;
}
case '60': {
command_len = 2;
data.overheatingEvents = {
events: parseInt(commands[i + 1], 16),
temperature: parseInt(commands[i + 2], 16),
};
break;
}
case '70': {
command_len = 2;
data.overheatingRecoveryTime = (parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16);
break;
}
case 'b1': {
command_len = 1;
data.relayState = parseInt(commands[i + 1], 16) === 0x01;
break;
}
case 'a0': {
command_len = 4;
const fuota_address = parseInt(
`${commands[i + 1]}${commands[i + 2]}${commands[i + 3]}${commands[i + 4]}`,
16
);
const fuota_address_raw = `${commands[i + 1]}${commands[i + 2]}${commands[i + 3]}${commands[i + 4]}`;
data.fuota = { fuota_address, fuota_address_raw };
break;
}
case 'a4': {
command_len = 1;
data.region = parseInt(commands[i + 1], 16);
break;
}
default:
break;
}
commands.splice(i, command_len);
});

return data;
};

if (bytes[0] === 1) {
data = handleKeepalive(bytes, data);
} else {
data = handleResponse(bytes, data);
bytes = bytes.slice(-3);
data = handleKeepalive(bytes, data);
}

return { data };
} catch (e) {
console.log(e);
throw new Error('Unhandled data');
}
};
Binary file added vendor/mclimate/16ads.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 133 additions & 0 deletions vendor/mclimate/16ads.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: 16A Dry Switch (16ADS)
description: The МClimate 16ADS is a miniature device that features a 16A dry relay. The device is small enough to fit behind most wall switches and outlets. The device operates in LoRaWAN Class C, features FUOTA (Firmware Upgrades Over The Air) and has overheating protection

# Hardware versions (optional, use when you have revisions)
hardwareVersions:
- version: '1.0'
numeric: 1

# Firmware versions (at least one is mandatory)
firmwareVersions:
- # Firmware version
version: '1.0'
numeric: 1
hardwareVersions:
- '1.0'

# LoRaWAN Device Profiles per region
# Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870
profiles:
EU863-870:
# Unique identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters)
id: 16ads-profile
lorawanCertified: false
codec: 16ads-codec

# Sensors that this device features (optional)
# Valid values are: accelerometer, altitude, auxiliary, barometer, battery, button, co2, distance, dust, gps, gyroscope,
# humidity, light, link, magnetometer, moisture, ph, pir, proximity, rssi, snr, sound, temperature, tvoc, velocity,
# vibration, water, wind direction and wind speed.
sensors:
- temperature
# Dimensions in mm (optional)
# Use width, height, length and/or diameter
dimensions:
width: 36
length: 18
height: 32

# Weight in grams (optional)
weight: 24

# Battery information (optional)
battery:
replaceable: false

# Operating conditions (optional)
operatingConditions:
# Temperature (Celsius)
temperature:
min: -10
max: 40
# Relative humidity (fraction of 1)
relativeHumidity:
min: 0.00
max: 0.80

# IP rating (optional)
ipCode: IP30

# Key provisioning (optional)
# Valid values are: custom (user can configure keys), join server and manifest.
keyProvisioning:
- custom
- manifest

# Key security (optional)
# Valid values are: none, read protected and secure element.
keySecurity: read protected

# Product and data sheet URLs (optional)
productURL: https://docs.mclimate.eu/mclimate-lorawan-devices/devices/mclimate-16a-dry-switch-16ads
dataSheetURL: https://3940008670-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-McDr-jr9h3qA888r1Yp%2Fuploads%2F4PSdg1EADYeBfci7DCmK%2FMClimate-16ADS-Datasheet.pdf?alt=media&token=8c95eed3-7cac-4859-855c-ba65ab3a6e5a
# resellerURLs:
# - name: 'Connected Things'
# region:
# - United Kingdom
# url: https://connectedthings.store/
# - name: 'Concept13'
# region:
# - United Kingdom
# url: https://www.concept13.co.uk
# - name: 'RG2i'
# region:
# - France
# url: https://connectedthings.store/
# - name: 'Wideco'
# region:
# - Sweden
# url: https://wideco.se
# - name: 'iioote'
# region:
# - Sweden
# url: https://iiooote.com

# Photos
photos:
main: 16aspm.png

# Regulatory compliances (optional)
compliances:
safety:
- body: IEC
norm: EN
standard: 62368-1:2020
- body: IEC
norm: EN
standard: 61010-1:2010
electromagneticCompatibility:
- body: ETSI
norm: EN
standard: 301489-1
version: 'latest'
- body: ETSI
norm: EN
standard: 301489-17
version: 'latest'
- body: CENELEC
norm: EN
standard: 55032:2015
- body: CENELEC
norm: EN
standard: 55035:2017
radioEquipment:
- body: ETSI
norm: EN
standard: 300220-1
- body: ETSI
norm: EN
standard: 300220-2
lowVoltageDirective:
- body: IEC
norm: EN
standard: 2014/35/EU
73 changes: 71 additions & 2 deletions vendor/mclimate/index.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,80 @@
endDevices:
- vicki
- t-valve
- flood-sensor
- vicki
- ht-sensor
- co2-sensor
- mc-button
- open-close
- wireless-thermostat
- co2-display
- co2-display-lite
- fan-coil-thermostat
- co2-display-lite
- 16aspm
- 16ads
profileIDs:
'0':
endDeviceID: 't-valve'
firmwareVersion: '6.0'
hardwareVersion: '2.3'
region: 'EU863-870'
'1':
endDeviceID: 'flood-sensor'
firmwareVersion: '6.0'
hardwareVersion: '2.4'
region: 'EU863-870'
'3':
endDeviceID: 'vicki'
firmwareVersion: '4.3'
hardwareVersion: '2.6'
region: 'EU863-870'
'4':
endDeviceID: 'ht-sensor'
firmwareVersion: '1.0'
hardwareVersion: '1.0'
region: 'EU863-870'
'5':
endDeviceID: 'co2-sensor'
firmwareVersion: '1.0'
hardwareVersion: '1.0'
region: 'EU863-870'
'6':
endDeviceID: 'mc-button'
firmwareVersion: '1.2'
hardwareVersion: '1.2'
region: 'EU863-870'
'7':
endDeviceID: 'open-close'
firmwareVersion: '1.2'
hardwareVersion: '1.3'
region: 'EU863-870'
'8':
endDeviceID: 'wireless-thermostat'
firmwareVersion: '1.5'
hardwareVersion: '3.0'
region: 'EU863-870'
'9':
endDeviceID: 'co2-display'
firmwareVersion: '1.1'
hardwareVersion: '2.6'
region: 'EU863-870'
'10':
endDeviceID: 'fan-coil-thermostat'
firmwareVersion: '1.0'
hardwareVersion: '1.1'
region: 'EU863-870'
'11':
endDeviceID: 'co2-display-lite'
firmwareVersion: '1.1'
hardwareVersion: '1.2'
region: 'EU863-870'
'13':
endDeviceID: '16aspm'
firmwareVersion: '1.2'
hardwareVersion: '1.3'
region: 'EU863-870'
'14':
endDeviceID: '16ads'
firmwareVersion: '1.0'
hardwareVersion: '1.0'
region: 'EU863-870'
13 changes: 13 additions & 0 deletions vendor/mclimate/mc-button-codec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
uplinkDecoder:
fileName: mc-button.js
examples:
- description: Short periodic uplink
input:
fPort: 2
bytes: [0x01, 0xAB, 0x02, 0x9B, 0x03]
output:
data:
sensorTemperature: 66.7
batteryVoltage: 3
thermistorProperlyConnected: true
pressEvent: 3
Loading
Loading